Wie sortiert man einen Vektor, der benutzerdefinierte (dh benutzerdefinierte) Objekte enthält?
Wahrscheinlich sollte eine Standard-STL-Algorithmus- Sortierung zusammen mit einem Prädikat (einer Funktion oder einem Funktionsobjekt) verwendet werden, das in einem der Felder (als Schlüssel zum Sortieren) im benutzerdefinierten Objekt ausgeführt wird.
Bin ich auf dem richtigen Weg?
248
Antworten:
Ein einfaches Beispiel mit
std::sort
Edit: Wie Kirill V. Lyadvinsky darauf hingewiesen, statt eine Art Prädikat liefert, können Sie das Gerät
operator<
fürMyStruct
:Mit dieser Methode können Sie den Vektor einfach wie folgt sortieren:
Edit2: Wie Kappa vorschlägt, können Sie den Vektor auch in absteigender Reihenfolge sortieren, indem Sie einen
>
Operator überladen und den Sortieraufruf ein wenig ändern:Und Sie sollten sort wie folgt nennen:
quelle
std::sort(vec.begin(), vec.end(), greater<MyStruct>())
, was sauber und elegant ist.#include <functional>
"std :: Greater " verwenden.operator<
entwederstd::sort(vec.begin(), vec.end());
oderstd::sort(vec.rbegin(), vec.rend());
abhängig davon haben können, ob Sie eine aufsteigende oder absteigende Reihenfolge haben möchten.Im Interesse der Berichterstattung. Ich habe eine Implementierung mit Lambda-Ausdrücken vorgeschlagen .
C ++ 11
C ++ 14
quelle
>
statt<
, um absteigende Reihenfolge zu erhalten.Sie könnten functor als drittes Argument verwenden
std::sort
oderoperator<
in Ihrer Klasse definieren .quelle
const
am Ende der Funktionssignatur hinzufügen ?const
.const
Schlüsselwort am Ende der Signatur gibt an, dass dieoperator()
Funktion die Instanz derXgreater
Struktur (die im Allgemeinen Elementvariablen haben kann) nicht ändert , während die Angabeconst
für die Eingabewerte nur angibt, dass diese Eingabewerte unveränderlich sind.Das Sortieren eines solchen
vector
oder eines anderen anwendbaren Bereichs (Iterator für veränderbare Eingaben) von benutzerdefinierten Objekten des TypsX
kann unter Verwendung verschiedener Methoden erreicht werden, insbesondere unter Verwendung der Verwendung von Standardbibliotheksalgorithmen wiesort
,stable_sort
,partial_sort
oderpartial_sort_copy
.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:
X
Objektbereichen eine häufige oder seltene Aufgabe (werden solche Bereiche an mehreren Stellen im Programm oder von Bibliotheksbenutzern sortiert)?X
Objektbereichen kinderleicht sein?Wenn das Sortieren von Bereichen
X
eine häufige Aufgabe ist und die erreichte Sortierung zu erwarten ist (dhX
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 (überladeneoperator()
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 verwendenLass
std::vector<X> vec_X;
undstd::vector<Y> vec_Y;
1. Überladen
T::operator<(T)
oderoperator<(T, T)
verwenden Sie Standardbibliotheksvorlagen, die keine Vergleichsfunktion erwarten.Entweder Überlastungsmitglied
operator<
:oder kostenlos
operator<
:2. Verwenden Sie einen Funktionszeiger mit einer benutzerdefinierten Vergleichsfunktion als Sortierfunktionsparameter.
3. Erstellen Sie eine
bool operator()(T, T)
Überladung für einen benutzerdefinierten Typ, der als Vergleichsfunktion übergeben werden kann.Diese Funktionsobjektdefinitionen können mit C ++ 11 und Vorlagen etwas allgemeiner geschrieben werden:
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.
Wobei C ++ 14 einen noch allgemeineren Lambda-Ausdruck ermöglicht:
die in ein Makro eingewickelt werden könnte
gewöhnliche Komparatorerstellung ganz reibungslos machen:
quelle
bool X_less(X const &l, X const &r) const { return l.i < r.i; }
für den Komparator geschrieben, aber dieconst
Schlüsselwörter sollten entfernt werden (da es sich nicht um eine Mitgliedsfunktion handelt).std::sort
oder ähnlich verwende, aber eine Instanz von benötigeCompare
, z. B. wenn ich a instanziierestd::set
?template<class T, class C> std::set<T, C> make_set(C const& compare) { return std::set<T, C>{ compare }; }
der wie folgt verwendet werden kannauto xset = make_set<X>([](auto && l, auto && r) { return l.i < r.i; });
.Du bist auf dem richtigen Weg.
std::sort
wirdoperator<
standardmäßig als Vergleichsfunktion verwendet. Um Ihre Objekte zu sortieren, müssen Sie entweder überladenbool operator<( const T&, const T& )
oder einen Funktor bereitstellen, der den Vergleich durchführt, ähnlich wie folgt:Der Vorteil der Verwendung eines Funktors besteht darin, dass Sie eine Funktion mit Zugriff auf die privaten Mitglieder der Klasse verwenden können.
quelle
operator<
ein Mitglied der Klasse (oder Struktur) zu machen, da global geschützte oder private Mitglieder verwendet werden könnten. Oder du solltest es zu einem Freund von Struktur C machenIch war neugierig, ob es messbare Auswirkungen auf die Leistung zwischen den verschiedenen Möglichkeiten gibt, wie man std :: sort aufrufen kann. Deshalb habe ich diesen einfachen Test erstellt:
Es erstellt einen zufälligen Vektor und misst dann, wie viel Zeit zum Kopieren und Sortieren der Kopie erforderlich ist (und berechnet eine Prüfsumme, um eine zu starke Eliminierung von totem Code zu vermeiden).
Ich habe mit g ++ (GCC) 7.2.1 20170829 (Red Hat 7.2.1-1) kompiliert.
Hier sind die Ergebnisse:
Es sieht so aus, als ob alle Optionen außer dem Übergeben des Funktionszeigers sehr ähnlich sind und das Übergeben eines Funktionszeigers + 30% Strafe verursacht.
Es sieht auch so aus, als ob der Operator <Version ~ 1% langsamer ist (ich habe den Test mehrmals wiederholt und der Effekt bleibt bestehen), was etwas seltsam ist, da dies darauf hindeutet, dass der generierte Code anders ist (mir fehlt die Fähigkeit, --save- zu analysieren). temporäre Ausgabe).
quelle
Ja,
std::sort()
mit dem dritten Parameter (Funktion oder Objekt) wäre es einfacher. Ein Beispiel: http://www.cplusplus.com/reference/algorithm/sort/quelle
In Ihrer Klasse können Sie den Operator "<" überladen.
quelle
Unten ist der Code mit Lambdas
quelle
quelle
Sie können eine benutzerdefinierte Komparatorklasse verwenden.
quelle
Um einen Vektor zu sortieren, können Sie den sort () -Algorithmus in verwenden.
Der dritte verwendete Parameter kann größer oder kleiner sein oder es kann auch eine beliebige Funktion oder ein beliebiges Objekt verwendet werden. Der Standardoperator ist jedoch <, wenn Sie den dritten Parameter leer lassen.
quelle
Wenn compare falsch ist, wird "swap" ausgeführt.
quelle