Co to jest operator <=> w C ++?

215

Chociaż starałem się dowiedzieć o c ++ operatorów, natknąłem się na dziwny operator porównania na cppreference.com , * w tabeli, która wyglądała tak:

wprowadź opis zdjęcia tutaj

„Cóż, jeśli są to popularne operatory w C ++, lepiej się ich uczę” - pomyślałem. Ale wszystkie moje próby wyjaśnienia tej tajemnicy zakończyły się niepowodzeniem. Nawet tutaj, na przepełnieniu stosu, nie miałem szczęścia w moich poszukiwaniach.

Czy istnieje związek między <=> a C ++ ?

A jeśli tak, to co dokładnie robi ten operator?

* W międzyczasie cppreference.com zaktualizowało tę stronę i teraz zawiera informacje o <=>operatorze.

qlp
źródło
82
@haccks: Och, proszę, mieliśmy mnóstwo pytań o rzeczy, które nawet nie zostały poddane głosowaniu w standardzie. Nie bez powodu mamy tag C ++ 20. Tego rodzaju rzeczy są bardzo tematyczne.
Nicol Bolas
1
@ cubuspl42 bar< foo::operator<=>jest przykładem tego, jak może być jak <--operator.
Yakk - Adam Nevraumont
8
@haccks: Racja. Podobnie jak C ++ 11 jest tagiem o kompilatorach, które implementują C ++ 11. A C ++ 14 jest tagiem o kompilatorach, które implementują C ++ 14. C ++ 17 dotyczy kompilatorów implementujących C ++ 17. Nie, C ++ 20 jest tagiem dla rzeczy o C ++ 20. A ponieważ to pytanie dotyczy C ++ 20, oto jest. Tag wiki, który był zły, a nie sam tag.
Nicol Bolas,

Odpowiedzi:

180

Nazywa się to operatorem porównania trójstronnego .

Zgodnie z propozycją papierową P0515 :

Pojawił się nowy trójdrożny operator porównania, <=>. Wyrażenie a <=> bzwraca obiekt, który porównuje <0if a < b, porównuje >0if a > bi porównuje ==0if ai bjest równy / równoważny.

Aby napisać wszystkie porównania dla twojego typu, po prostu napisz, operator<=>który zwraca odpowiedni typ kategorii:

  • Wróć się _ordering Jeśli typ naturalnie podpory <, a my skutecznie generować <, >, <=, >=, ==, i !=; w przeciwnym razie zwróci _ _equality , a my efektywnie wygenerujemy == i ! = .

  • Zwraca wartość silną, jeśli dla danego typu a == bimplikuje f(a) == f(b)(podstawialność, gdzie f odczytuje tylko stan istotny dla porównania dostępny przy użyciu niepowiązanego interfejsu const), w przeciwnym razie zwraca słaby.

Cppreference mówi:

Wyrażenia operatora porównania trójdrożnego mają postać

lhs <=> rhs   (1)  

Wyrażenie zwraca obiekt, który

  • porównuje <0jeślilhs < rhs
  • porównuje >0jeślilhs > rhs
  • i porównuje, ==0czy lhsi rhssą równe / równoważne.
msc
źródło
93
Dla tych, którzy są zdezorientowani (jak ja) na temat tego, co oznacza „porównuje <0”, „porównuje >0” i „porównuje ==0”, oznaczają <=>zwroty ujemne, dodatnie lub zerowe, w zależności od argumentów. Podobnie jak strncmpi memcmp.
Cornstalks
1
@Dai chociaż oba 'a' < 'a'i 'c' < 'a'oba fałszywe, 'a' < 'a'a 'a' < 'c'nie są. W porządkowaniu silnym następują następujące zasady: a != ba < b || b < a
Revolver_Ocelot
1
@Revolver_Ocelot Ah, więc można go zdefiniować / wygenerować jako operator==(T x, T y) { return !(x < y) && !(y < x); }i operator!=(T x, T y) { return (x < y) || (y < x); }- ah-ha! Oczywiście jest to mniej wydajne niż prawdziwe, ==ponieważ wywołuje porównanie dwukrotnie, ale nadal jest porządne.
Dai
3
Co oznaczają „powrót silny” i „powrót słaby”?
lucidbrot
2
@hkBattousai oznacza to, że obiekt zwraca, gdy porównany < 0jest prawdziwy. Oznacza to, że jeśli a < bwtedy (a <=> b) < 0zawsze jest to prawda.
rmobis
116

W dniu 11.11.2017 r. Komitet ISO C ++ przyjął propozycję Herb Suttera dotyczącą trójstronnego operatora porównania <=> „statku kosmicznego” jako jedną z nowych funkcji dodanych do C ++ 20 . W artykule zatytułowanym Spójne porównanie Sutter, Maurer i Brown demonstrują koncepcje nowego projektu. Aby zapoznać się z wnioskiem, oto fragment tego artykułu:

Wyrażenie a <=> b zwraca obiekt, który porównuje <0 jeśli a <b , porównuje > 0 jeśli a> b , i porównuje == 0, jeśli aib są równe / równoważne.

Typowy przypadek: pisanie wszystkich porównań dla twojego typu X z typem YTypowy , z semantyką składową, po prostu napisz:

auto X::operator<=>(const Y&) =default;

Przypadki zaawansowane: aby napisać wszystkie porównania dla twojego typu X z typem Y , po prostu napisz operator <=>, który przyjmuje Y , może użyć = default, aby uzyskać semantykę członkowską w razie potrzeby, i zwraca odpowiedni typ kategorii:

  • Zwróć _ordering, jeśli Twój typ naturalnie obsługuje < , a my efektywnie wygenerujemy symetryczne < , > , <= , > = , == i ! = ; w przeciwnym razie zwróci _ _equality , a my efektywnie wygenerujemy symetryczny == i ! = .
  • Zwróć wartość strong_, jeśli dla twojego typu a == b oznacza f (a) == f (b) (substytucyjność, gdzie f odczytuje tylko stan istotny dla porównania, który jest dostępny przy użyciu publicznych elementów stałych ), w przeciwnym razie zwraca wartość słabe_ .

Kategorie porównawcze

Pięć kategorii porównawczych jest zdefiniowanych jako std::typy, z których każda ma następujące predefiniowane wartości:

+--------------------------------------------------------------------+
|                  |          Numeric  values          | Non-numeric |
|     Category     +-----------------------------------+             |
|                  | -1   | 0          | +1            |   values    |
+------------------+------+------------+---------------+-------------+
| strong_ordering  | less | equal      | greater       |             |
| weak_ordering    | less | equivalent | greater       |             |
| partial_ordering | less | equivalent | greater       | unordered   |
| strong_equality  |      | equal      | nonequal      |             |
| weak_equality    |      | equivalent | nonequivalent |             |
+------------------+------+------------+---------------+-------------+

Niejawne konwersje między tymi typami są zdefiniowane w następujący sposób:

  • strong_orderingz wartościami { less, equal, greater} niejawnie konwertuje do:
    • weak_orderingo wartościach { less, equivalent, greater}
    • partial_orderingo wartościach { less, equivalent, greater}
    • strong_equalityo wartościach { unequal, equal, unequal}
    • weak_equalityo wartościach { nonequivalent, equivalent, nonequivalent}
  • weak_orderingz wartościami { less, equivalent, greater} niejawnie konwertuje do:
    • partial_orderingo wartościach { less, equivalent, greater}
    • weak_equalityo wartościach { nonequivalent, equivalent, nonequivalent}
  • partial_orderingz wartościami { less, equivalent, greater, unordered} niejawnie konwertuje do:
    • weak_equalityo wartościach { nonequivalent, equivalent, nonequivalent, nonequivalent}
  • strong_equalityz wartościami { equal, unequal} domyślnie konwertuje się na:
    • weak_equalityz wartościami { equivalent, nonequivalent}

Porównanie trójstronne

<=>Token wprowadzony. Sekwencja znaków <=>symbolizuje <= >w starym kodzie źródłowym. Na przykład X<&Y::operator<=>musi dodać spację, aby zachować swoje znaczenie.

Przeciążalny operator <=>jest trójstronną funkcją porównania i ma pierwszeństwo wyższe niż <i niższe niż <<. Zwraca typ, który można porównać z literałem, 0ale dozwolone są inne typy zwracania, takie jak obsługa szablonów wyrażeń. Wszystko<=> operatory zdefiniowane w języku i standardowej bibliotece zwracają jeden z 5 wyżej wymienionych std::typów kategorii porównawczych.

W przypadku typów języków dostępne są następujące wbudowane <=>porównania tego samego typu. Wszystkie są constexpr , chyba że zaznaczono inaczej. Tych porównań nie można wywoływać heterogenicznie przy użyciu skalarnych promocji / konwersji.

  • Dla bool, integralny i typy wskaźnik, <=>zwrotówstrong_ordering .
  • W przypadku typów wskaźników różne kwalifikacje cv i konwersje pochodne do bazy mogą wywoływać jednorodne wbudowane <=>, a wbudowane są heterogeniczne operator<=>(T*, nullptr_t). Jedynie porównania wskaźników do tego samego obiektu / alokacji są wyrażeniami stałymi.
  • W przypadku podstawowych typów zmiennoprzecinkowych <=>zwraca partial_orderingi można je heterogenicznie wywoływać, rozszerzając argumenty na większy typ zmiennoprzecinkowy.
  • W przypadku wyliczeń <=>zwraca to samo, co typ bazowy wyliczenia <=>.
  • Dla nullptr_t, <=>zwrotów strong_orderingi zawsze plonów equal.
  • W przypadku tablic możliwych do skopiowania T[N] <=> T[N]zwraca ten sam typ, co T„s” <=>i dokonuje porównania leksykograficznego z zastosowaniem elementów. Nie ma <=>innych tablic.
  • Bo voidnie ma <=>.

Aby lepiej zrozumieć wewnętrzne funkcjonowanie tego operatora, należy zapoznać się z oryginalną papier . Właśnie tego się dowiedziałem za pomocą wyszukiwarek.

qlp
źródło
1
Jakby procesor nie był już wystarczająco skomplikowany. Może po prostu napisz metodę porównawczą ...
Leandro
6
@Leandro Operatorem statku kosmicznego jest ta metoda porównania. Dodatkowo po prostu działa i zapisuje (lub usuwa) sześć innych operatorów porównania. Przyjmę jedną funkcję operatora porównania zapisaną na sześciu pojedynczych płytach kotłowych.
anonimowy
Zauważ, że _equalitytypy umarły: okazało się, że <=>gra dobrze z czterema operatorami relacyjnymi, ale nie tak dobrze z dwoma operatorami równości (chociaż istnieje intensywny cukier syntaktyczny na poparcie typowego przypadku, w którym chcesz je wszystkie).
Davis Herring
12

Ta odpowiedź stała się nieistotna od czasu zmiany strony, do której się odwołuje

Witryna, do której się odwołujesz, została uszkodzona. Tego dnia było dużo edytowane i różne części nie były zsynchronizowane. Status, na który patrzyłem, to:

U góry strony znajduje się lista istniejących operatorów porównania (w C ++ 14). Nie ma <=>tam

Na dole strony powinni wymienić tych samych operatorów, ale wygłupili się i dodali tę przyszłą sugestię.

gccjeszcze nie wie o tym <=>(i z -std=c++14, nigdy nie będzie), więc myśli, że miałeś na myśli a <= > b. To wyjaśnia komunikat o błędzie.

Jeśli spróbujesz tego samego za pięć lat, prawdopodobnie otrzymasz lepszy komunikat o błędzie, coś w rodzaju <=> not part of C++14.

Stig Hemmer
źródło
1
Strona internetowa, do której prowadzi łącze OP, jest poprawna, podobnie jak osobna strona, do której prowadzi link. To kwalifikuje <=>operatora etykietą (od C ++ 20), która informuje, w której wersji standardu można się spodziewać. Etykietowanie standardów to konwencja, którą przestrzega cppreference.com. Oczywiście nie masz kompilatora, który powrócił w maszynie czasu, aby go obsługiwać, ale cpprefernce mówi (poprawnie), czego się spodziewać.
Spencer
Tak, ale ... Brak odpowiedzi. Komentujesz ... czy coś.
qlp
2
Zamierzałem utworzyć link do tej samej strony internetowej, co pytanie, ale przeoczyłem. Myślę, że odpowiedziałem na części pytania, na które inne odpowiedzi nie. Zignorowałem główne odważne pytanie, ponieważ inni już na to odpowiedzieli.
Stig Hemmer