Jak można posortować wektor zawierający obiekty niestandardowe (tj. Zdefiniowane przez użytkownika).
Prawdopodobnie należy użyć standardowego algorytmu sortowania STL wraz z predykatem (funkcją lub obiektem funkcji), który działałby na jednym z pól (jako klucz do sortowania) w obiekcie niestandardowym.
Czy jestem na dobrej drodze?
248
Odpowiedzi:
Prosty przykład użycia
std::sort
Edit: Jak Kirill V. Lyadvinsky wskazał, zamiast dostarczania predykat sortowania, można wdrożyć
operator<
naMyStruct
:Dzięki tej metodzie możesz po prostu posortować wektor w następujący sposób:
Edycja2: Jak sugeruje Kappa, możesz również sortować wektor w malejącej kolejności, przeciążając
>
operatora i zmieniając nieco sortowanie wywołania:I powinieneś nazywać sort jako:
źródło
std::sort(vec.begin(), vec.end(), greater<MyStruct>())
która jest czysta i elegancka.#include <functional>
użyć „std :: greater”.operator<
i użyjstd::sort(vec.begin(), vec.end());
lubstd::sort(vec.rbegin(), vec.rend());
w zależności od tego, czy chcesz mieć rosnąco lub malejąco.W interesie pokrycia. Przedstawiłem implementację przy użyciu wyrażeń lambda .
C ++ 11
C ++ 14
źródło
>
zamiast,<
aby uzyskać malejącą kolejność.Możesz użyć funktora jako trzeciego argumentu
std::sort
lub zdefiniowaćoperator<
w swojej klasie.źródło
const
podpis na końcu funkcji?const
.const
kluczowe na końcu podpisu określa, żeoperator()
funkcja nie zmienia wystąpieniaXgreater
struktury (która ogólnie może mieć zmienne składowe), natomiast wskazanieconst
wartości wejściowych określa tylko, że te wartości wejściowe są niezmienne.Sortowanie takiego
vector
lub dowolnego stosownego (niestandardowego iteratora wejściowego) typu obiektów niestandardowychX
można osiągnąć za pomocą różnych metod, w tym w szczególności za pomocą standardowych algorytmów bibliotecznych, takich jaksort
,stable_sort
,partial_sort
lubpartial_sort_copy
.Ponieważ większość technik mających na celu uzyskanie względnego uporządkowania
X
elementów została już opublikowana, zacznę od kilku uwag na temat „dlaczego” i „kiedy”, aby zastosować różne podejścia.Podejście „najlepsze” będzie zależeć od różnych czynników:
X
obiektów jest powszechnym czy rzadkim zadaniem (czy takie zakresy będą sortowane na wiele różnych miejsc w programie czy przez użytkowników biblioteki)?X
obiektów powinny być niezawodne?Jeśli zakresy sortowania
X
są powszechnym zadaniem i należy oczekiwać osiągniętego sortowania (tj. PoX
prostu otacza jedną podstawową wartość), to prawdopodobnie przejdzie on do przeciążenia,operator<
ponieważ umożliwia sortowanie bez żadnych zakłóceń (jak prawidłowe przekazywanie odpowiednich komparatorów) i wielokrotne oczekiwane wyniki wyniki.Jeśli sortowanie jest częstym zadaniem lub prawdopodobnie będzie wymagane w różnych kontekstach, ale istnieje wiele kryteriów, które można zastosować do sortowania
X
obiektów, wybrałbym Functors (przeciążoneoperator()
funkcje klas niestandardowych) lub wskaźniki funkcji (tj. Jeden funktor / funkcja dla porządku leksykalnego i innego dla porządku naturalnego).Jeśli zakresy sortowania typów
X
są rzadkie lub mało prawdopodobne w innych kontekstach, zwykle używam lambdów zamiast zaśmiecać przestrzeń nazw większą liczbą funkcji lub typów.Jest to szczególnie prawdziwe, jeśli sortowanie nie jest w jakiś sposób „jasne” lub „naturalne”. Możesz łatwo zrozumieć logikę
operator<
związaną z porządkowaniem, patrząc na lambda, która jest stosowana w miejscu, podczas gdy na pierwszy rzut oka jest niejasna i trzeba by sprawdzić definicję, aby wiedzieć, która logika porządkowania zostanie zastosowana.Zauważ jednak, że jedna
operator<
definicja jest pojedynczym punktem awarii, podczas gdy wiele lambas jest wieloma punktami awarii i wymaga większej ostrożności.Jeśli definicja
operator<
nie jest dostępna w miejscu, w którym odbywa się sortowanie / kompilowany jest szablon sortowania, kompilator może zostać zmuszony do wywołania funkcji podczas porównywania obiektów, zamiast wprowadzania logiki porządkowania, co może być poważną wadą (przynajmniej gdy optymalizacja czasu łącza / generowanie kodu nie jest stosowane).Sposoby osiągnięcia porównywalności
class X
w celu użycia standardowych algorytmów sortowania bibliotekNiech
std::vector<X> vec_X;
istd::vector<Y> vec_Y;
1. Przeciąż
T::operator<(T)
luboperator<(T, T)
użyj standardowych szablonów bibliotek, które nie oczekują funkcji porównania.Członek przeciążenia
operator<
:lub za darmo
operator<
:2. Użyj wskaźnika funkcji z niestandardową funkcją porównania jako parametru funkcji sortowania.
3. Utwórz
bool operator()(T, T)
przeciążenie dla typu niestandardowego, który można przekazać jako funktor porównawczy.Te definicje obiektów funkcji można zapisać nieco bardziej ogólnie, używając C ++ 11 i szablonów:
które mogą być użyte do sortowania dowolnego typu z
i
obsługą elementów<
.4. Przekaż zamknięcie anonimowe (lambda) jako parametr porównawczy do funkcji sortujących.
Gdzie C ++ 14 umożliwia jeszcze bardziej ogólne wyrażenie lambda:
które można zawinąć w makro
dzięki czemu tworzenie zwykłego komparatora jest dość płynne:
źródło
bool X_less(X const &l, X const &r) const { return l.i < r.i; }
dla komparatora, aleconst
słowa kluczowe powinny zostać usunięte (ponieważ nie jest to funkcja członkowska).std::sort
lub podobna, ale potrzebuje instancjiCompare
np. Podczas tworzenia instancjistd::set
?template<class T, class C> std::set<T, C> make_set(C const& compare) { return std::set<T, C>{ compare }; }
który może być użyty jakauto xset = make_set<X>([](auto && l, auto && r) { return l.i < r.i; });
.Jesteś na dobrej drodze.
std::sort
będzieoperator<
domyślnie używał jako funkcji porównania. Aby więc posortować obiekty, będziesz musiał albo przeładować,bool operator<( const T&, const T& )
albo podać funktor , który dokona porównania, podobnie jak to:Zaletą użycia funktora jest to, że można użyć funkcji z dostępem do prywatnych członków klasy.
źródło
operator<
członka klasy (lub struktury), ponieważ globalny mógłby używać chronionych lub prywatnych członków. Albo powinieneś uczynić go przyjacielem struktury C.Byłem ciekawy, czy istnieje jakiś wymierny wpływ na wydajność między różnymi sposobami nazywania std :: sort, dlatego stworzyłem ten prosty test:
Tworzy losowy wektor, a następnie mierzy, ile czasu potrzeba na jego skopiowanie i posortowanie jego kopii (i obliczenie pewnej sumy kontrolnej, aby uniknąć zbyt energicznej eliminacji martwego kodu).
Kompilowałem przy użyciu g ++ (GCC) 7.2.1 20170829 (Red Hat 7.2.1-1)
Oto wyniki:
Wygląda na to, że wszystkie opcje z wyjątkiem przekazywania wskaźnika funkcji są bardzo podobne, a przekazanie wskaźnika funkcji powoduje + 30% kary.
Wygląda również na to, że operator <wersja jest ~ 1% wolniejszy (powtórzyłem test wiele razy, a efekt utrzymuje się), co jest nieco dziwne, ponieważ sugeruje, że wygenerowany kod jest inny (brak umiejętności analizy - zapisywania - wyjście temps).
źródło
Tak,
std::sort()
z trzecim parametrem (funkcją lub obiektem) byłoby łatwiej. Przykład: http://www.cplusplus.com/reference/algorithm/sort/źródło
W swojej klasie możesz przeciążać operatora „<”.
źródło
Poniżej znajduje się kod używający lambdas
źródło
źródło
Możesz użyć zdefiniowanej przez użytkownika klasy komparatora.
źródło
Aby posortować wektor, możesz użyć algorytmu sort ().
Trzeci użyty parametr może być większy lub mniejszy lub można również użyć dowolnej funkcji lub obiektu. Jednak domyślnym operatorem jest <jeśli pozostawisz trzeci parametr pusty.
źródło
jeśli porównanie jest fałszywe, wykona „zamianę”.
źródło