Das Sortieren eines solchen vector
oder eines anderen anwendbaren Bereichs (Iterator für veränderbare Eingaben) von benutzerdefinierten Objekten des Typs X
kann unter Verwendung verschiedener Methoden erreicht werden, insbesondere unter Verwendung der Verwendung von Standardbibliotheksalgorithmen wie
Da die meisten Techniken, um eine relative Reihenfolge der X
Elemente zu erhalten, bereits veröffentlicht wurden, beginne ich mit einigen Anmerkungen zum "Warum" und "Wann", um die verschiedenen Ansätze zu verwenden.
Der "beste" Ansatz hängt von verschiedenen Faktoren ab:
- Ist das Sortieren von
X
Objektbereichen eine häufige oder seltene Aufgabe (werden solche Bereiche an mehreren Stellen im Programm oder von Bibliotheksbenutzern sortiert)?
- Ist die erforderliche Sortierung "natürlich" (erwartet) oder gibt es mehrere Möglichkeiten, den Typ mit sich selbst zu vergleichen?
- Ist die Leistung ein Problem oder sollte das Sortieren von
X
Objektbereichen kinderleicht sein?
Wenn das Sortieren von Bereichen X
eine häufige Aufgabe ist und die erreichte Sortierung zu erwarten ist (dh X
nur einen einzelnen Grundwert umschließt), würde on wahrscheinlich überladen, operator<
da es das Sortieren ohne Fuzz ermöglicht (wie das korrekte Übergeben geeigneter Komparatoren) und wiederholt erwartete Ausbeuten ermöglicht Ergebnisse.
Wenn das Sortieren eine gemeinsame Aufgabe ist oder wahrscheinlich in verschiedenen Kontexten erforderlich ist, es jedoch mehrere Kriterien gibt, anhand derer X
Objekte sortiert werden können, würde ich mich für Functors (überladene operator()
Funktionen benutzerdefinierter Klassen) oder Funktionszeiger (dh einen Funktor / eine Funktion) entscheiden für die lexikalische Bestellung und eine andere für die natürliche Bestellung).
Wenn das Sortieren von X
Typbereichen in anderen Kontexten ungewöhnlich oder unwahrscheinlich ist, verwende ich eher Lambdas, anstatt einen Namespace mit mehr Funktionen oder Typen zu überladen.
Dies gilt insbesondere dann, wenn die Sortierung in irgendeiner Weise nicht "klar" oder "natürlich" ist. Sie können die Logik hinter der Bestellung leicht erkennen, wenn Sie ein Lambda betrachten, das an Ort operator<
und Stelle angewendet wird, während es auf den ersten Blick opag ist, und Sie müssten die Definition nachschlagen, um zu wissen, welche Ordnungslogik angewendet wird.
Beachten Sie jedoch, dass eine einzelne operator<
Definition ein einzelner Fehlerpunkt ist, während mehrere Lambas mehrere Fehlerpunkte sind und eine größere Vorsicht erfordern.
Wenn die Definition von operator<
nicht verfügbar ist, wo die Sortierung durchgeführt wird / die Sortiervorlage kompiliert wird, kann der Compiler gezwungen sein, beim Vergleichen von Objekten einen Funktionsaufruf auszuführen, anstatt die Ordnungslogik einzuschleusen, was ein schwerwiegender Nachteil sein kann (zumindest wenn) Verbindungszeitoptimierung / Codegenerierung wird nicht angewendet).
Möglichkeiten zur Vergleichbarkeit class X
, um Standard-Bibliothekssortieralgorithmen zu verwenden
Lass std::vector<X> vec_X;
undstd::vector<Y> vec_Y;
1. Überladen T::operator<(T)
oder operator<(T, T)
verwenden Sie Standardbibliotheksvorlagen, die keine Vergleichsfunktion erwarten.
Entweder Überlastungsmitglied operator<
:
struct X {
int i{};
bool operator<(X const &r) const { return i < r.i; }
};
// ...
std::sort(vec_X.begin(), vec_X.end());
oder kostenlos operator<
:
struct Y {
int j{};
};
bool operator<(Y const &l, Y const &r) { return l.j < r.j; }
// ...
std::sort(vec_Y.begin(), vec_Y.end());
2. Verwenden Sie einen Funktionszeiger mit einer benutzerdefinierten Vergleichsfunktion als Sortierfunktionsparameter.
struct X {
int i{};
};
bool X_less(X const &l, X const &r) { return l.i < r.i; }
// ...
std::sort(vec_X.begin(), vec_X.end(), &X_less);
3. Erstellen Sie eine bool operator()(T, T)
Überladung für einen benutzerdefinierten Typ, der als Vergleichsfunktion übergeben werden kann.
struct X {
int i{};
int j{};
};
struct less_X_i
{
bool operator()(X const &l, X const &r) const { return l.i < r.i; }
};
struct less_X_j
{
bool operator()(X const &l, X const &r) const { return l.j < r.j; }
};
// sort by i
std::sort(vec_X.begin(), vec_X.end(), less_X_i{});
// or sort by j
std::sort(vec_X.begin(), vec_X.end(), less_X_j{});
Diese Funktionsobjektdefinitionen können mit C ++ 11 und Vorlagen etwas allgemeiner geschrieben werden:
struct less_i
{
template<class T, class U>
bool operator()(T&& l, U&& r) const { return std::forward<T>(l).i < std::forward<U>(r).i; }
};
Dies kann verwendet werden, um jeden Typ mit i
unterstützendem Element zu sortieren <
.
4. Übergeben Sie einen Anonymus-Verschluss (Lambda) als Vergleichsparameter an die Sortierfunktionen.
struct X {
int i{}, j{};
};
std::sort(vec_X.begin(), vec_X.end(), [](X const &l, X const &r) { return l.i < r.i; });
Wobei C ++ 14 einen noch allgemeineren Lambda-Ausdruck ermöglicht:
std::sort(a.begin(), a.end(), [](auto && l, auto && r) { return l.i < r.i; });
die in ein Makro eingewickelt werden könnte
#define COMPARATOR(code) [](auto && l, auto && r) -> bool { return code ; }
gewöhnliche Komparatorerstellung ganz reibungslos machen:
// sort by i
std::sort(v.begin(), v.end(), COMPARATOR(l.i < r.i));
// sort by j
std::sort(v.begin(), v.end(), COMPARATOR(l.j < r.j));