Cos'è l'operatore <=> in C ++?


217

Mentre stavo cercando di conoscere gli operatori C ++ , mi sono imbattuto in uno strano operatore di confronto su cppreference.com , * in una tabella che assomigliava a questa:

inserisci qui la descrizione dell'immagine

"Bene, se questi sono operatori comuni in C ++, è meglio che li impari", ho pensato. Ma tutti i miei tentativi di chiarire questo mistero non hanno avuto successo. Anche qui, su Stack Overflow non ho avuto fortuna nella mia ricerca.

Esiste una connessione tra <=> e C ++ ?

E se c'è, cosa fa esattamente questo operatore?

* Nel frattempo cppreference.com ha aggiornato quella pagina e ora contiene informazioni sull'operatore <=>.


1
@ cubuspl42 bar< foo::operator<=>è un esempio di come potrebbe essere come l' <--operatore.
Yakk - Adam Nevraumont

8
@haccks: giusto. Come C ++ 11 è un tag sui compilatori che implementano C ++ 11. E C ++ 14 è un tag sui compilatori che implementano C ++ 14. E C ++ 17 riguarda i compilatori che implementano C ++ 17. No, il C ++ 20 è il tag per cose su C ++ 20. E poiché questa domanda riguarda C ++ 20, eccola. Il tag wiki che era sbagliato, non il tag stesso.
Nicol Bolas

Risposte:


179

Questo è chiamato operatore di confronto a tre vie .

Secondo la proposta di carta P0515 :

C'è un nuovo operatore di confronto a tre vie, <=>. L'espressione a <=> brestituisce un oggetto che confronta <0se a < b, confronta >0se a > be confronta ==0se ae bsono uguali / equivalenti.

Per scrivere tutti i confronti per il tuo tipo, scrivi operator<=>che restituisca il tipo di categoria appropriato:

  • Restituisce un _ordering se il tipo naturalmente supporti <, e noi in modo efficiente generiamo <, >, <=, >=, ==, e !=; altrimenti restituiremo una _equality e genereremo in modo efficiente == e ! = .

  • Restituisce forte se per il tuo tipo a == bimplica f(a) == f(b)(sostituibilità, dove f legge solo lo stato saliente del confronto accessibile utilizzando l'interfaccia const non privata), altrimenti restituisce debole.

Il riferimento cpp dice:

Le espressioni dell'operatore di confronto a tre vie hanno la forma

lhs <=> rhs   (1)  

L'espressione restituisce un oggetto che

  • confronta <0selhs < rhs
  • confronta >0selhs > rhs
  • e confronta ==0se lhse rhssono uguali / equivalenti.

99
Per coloro che sono confusi (come me) sul significato di "confronta <0", "confronta >0" e "confronta ==0", intendono <=>restituire un valore negativo, positivo o zero, a seconda degli argomenti. Molto simile a strncmpe memcmp.
Steli di mais

1
@Dai anche se entrambi 'a' < 'a'e 'c' < 'a'sono entrambi falsi 'a' < 'a'e 'a' < 'c'non lo sono. IN forte ordine seguente è vero: a != ba < b || b < a
Revolver_Ocelot

1
@Revolver_Ocelot Ah, quindi può essere definito / generato come operator==(T x, T y) { return !(x < y) && !(y < x); }e operator!=(T x, T y) { return (x < y) || (y < x); }- ah-ha! Ovviamente questo è meno efficiente di un vero ==in quanto invoca il confronto due volte, ma comunque pulito.
Dai

3
Cosa significano "return strong" e "return weak"?
lucidbrot

2
@hkBattousai significa che l'oggetto ritorna, se confrontato restituisce < 0true. Cioè, se a < ballora (a <=> b) < 0è sempre vero.
rmobis

118

L' 11-11-2017 , il comitato ISO C ++ ha adottato la proposta di Herb Sutter per l' operatore di confronto a tre vie <=> "astronave" come una delle nuove funzionalità aggiunte a C ++ 20 . Nel documento intitolato Confronto coerente Sutter, Maurer e Brown dimostrano i concetti del nuovo design. Per una panoramica della proposta, ecco un estratto dall'articolo:

L'espressione a <=> b restituisce un oggetto che confronta <0 se a <b , confronta > 0 se a> be confronta == 0 se aeb sono uguali / equivalenti.

Caso comune: per scrivere tutti i confronti per il tipo X con il tipo Y , con semantica a livello di membro, è sufficiente scrivere:

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

Casi avanzati: per scrivere tutti i confronti per il tuo tipo X con il tipo Y , scrivi l' operatore <=> che accetta una Y , puoi usare = default per ottenere la semantica a livello di membro se lo desideri e restituisce il tipo di categoria appropriato:

  • Restituisce un _ordering se il tuo tipo supporta naturalmente < e genereremo in modo efficiente < , > , <= , > = , == e ! = ; Simmetrico ; altrimenti restituisce una _equality e genereremo in modo efficiente == e ! = simmetrici .
  • Restituisce strong_ se per il tuo tipo a == b implica f (a) == f (b) (sostituibilità, dove f legge solo lo stato saliente del confronto accessibile utilizzando i membri const pubblici ), altrimenti restituisci weak_ .

Categorie di confronto

Cinque categorie di confronto sono definite come std::tipi, ciascuna con i seguenti valori predefiniti:

+--------------------------------------------------------------------+
|                  |          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 |             |
+------------------+------+------------+---------------+-------------+

Le conversioni implicite tra questi tipi sono definite come segue:

  • strong_orderingcon i valori { less, equal, greater} converte implicitamente a:
    • weak_orderingcon i valori { less, equivalent, greater}
    • partial_orderingcon i valori { less, equivalent, greater}
    • strong_equalitycon i valori { unequal, equal, unequal}
    • weak_equalitycon i valori { nonequivalent, equivalent, nonequivalent}
  • weak_orderingcon i valori { less, equivalent, greater} converte implicitamente a:
    • partial_orderingcon i valori { less, equivalent, greater}
    • weak_equalitycon i valori { nonequivalent, equivalent, nonequivalent}
  • partial_orderingcon i valori { less, equivalent, greater, unordered} converte implicitamente a:
    • weak_equalitycon i valori { nonequivalent, equivalent, nonequivalent, nonequivalent}
  • strong_equalitycon valori { equal, unequal} converte implicitamente in:
    • weak_equalitycon valori { equivalent, nonequivalent}

Confronto a tre vie

Il <=>token viene introdotto. La sequenza di caratteri si <=>tokenizza <= >, nel vecchio codice sorgente. Ad esempio, è X<&Y::operator<=>necessario aggiungere uno spazio per mantenere il suo significato.

L'operatore sovraccaricabile <=>è una funzione di confronto a tre vie e ha la precedenza maggiore di <e minore di <<. Restituisce un tipo che può essere confrontato con il letterale 0ma sono consentiti altri tipi restituiti, ad esempio per supportare i modelli di espressione. Tutti gli <=>operatori definiti nella lingua e nella libreria standard restituiscono uno dei 5 std::tipi di categorie di confronto sopra menzionati .

Per i tipi di linguaggio, <=>vengono forniti i seguenti confronti incorporati dello stesso tipo. Tutti sono constexpr , tranne dove diversamente indicato. Questi confronti non possono essere richiamati in modo eterogeneo utilizzando promozioni / conversioni scalari.

  • Per bool, integrale e tipi di puntatore, <=>restituisce strong_ordering.
  • Per i tipi di puntatore, le diverse qualifiche cv e le conversioni da derivato a base possono richiamare un built-in omogeneo <=>, e ci sono eterogenei incorporati operator<=>(T*, nullptr_t). Solo i confronti di puntatori allo stesso oggetto / allocazione sono espressioni costanti.
  • Per i tipi a virgola mobile fondamentali, <=>restituisce partial_orderinge può essere richiamato in modo eterogeneo allargando gli argomenti a un tipo a virgola mobile più grande.
  • Per le enumerazioni, <=>restituisce lo stesso del tipo sottostante dell'enumerazione <=>.
  • Per nullptr_t, <=>ritorna strong_orderinge sempre produce equal.
  • Per gli array copiabili, T[N] <=> T[N]restituisce lo stesso tipo T's <=>comparatore elementwise lessicografico ed esegue. Non ce n'è <=>per altri array.
  • Perché voidnon c'è <=>.

Per comprendere meglio il funzionamento interno di questo operatore, leggere il documento originale . Questo è proprio quello che ho scoperto usando i motori di ricerca.


2
Come se cpp non fosse già abbastanza complesso. Perché non scrivere semplicemente un metodo di confronto ...
Leandro

9
@Leandro L'operatore della nave spaziale è quel metodo di confronto. Inoltre, funziona e scrive (o elimina) gli altri sei operatori di confronto. Prenderò una funzione operatore di confronto scritta su sei singoli boilerplate.
anonimo

Nota che i _equalitytipi sono morti: si è scoperto che funziona <=>bene con i quattro operatori relazionali ma non altrettanto bene con i due operatori di uguaglianza (anche se c'è un po 'di zucchero sintattico intenso per supportare il caso comune in cui li vuoi tutti).
Davis Herring

12

Questa risposta è diventata irrilevante poiché la pagina Web di riferimento è cambiata

La pagina web a cui fai riferimento è stata interrotta. Era stato modificato molto quel giorno e diverse parti non erano sincronizzate. Lo stato quando lo stavo guardando era:

Nella parte superiore della pagina elenca gli operatori di confronto attualmente esistenti (in C ++ 14). Non <=>c'è.

In fondo alla pagina, avrebbero dovuto elencare gli stessi operatori, ma hanno sbagliato e hanno aggiunto questo suggerimento futuro.

gccnon sa <=>ancora (e con -std=c++14, mai lo saprà), quindi pensa che intendevi a <= > b. Questo spiega il messaggio di errore.

Se provi la stessa cosa tra cinque anni, probabilmente riceverai un messaggio di errore migliore, qualcosa di simile <=> not part of C++14.


1
La pagina web a cui si collega l'OP è corretta, così come la pagina separata a cui ci si collega. Qualifica l' <=>operatore con l'etichetta (dal C ++ 20), indicando in quale versione dello standard aspettarselo. L'etichettatura degli standard è una convenzione seguita da cppreference.com. Ovviamente non hai un compilatore che è tornato in una macchina del tempo per supportarlo, ma cpprefernce ti dice (correttamente) cosa aspettarti.
Spencer

Sì, ma ... Non una risposta. Stai commentando ... o qualcosa del genere.
qlp

2
Avevo intenzione di collegarmi alla stessa pagina web della domanda, ma non l'ho vista. Penso di aver risposto alle parti della domanda che altre risposte non hanno fatto. Ho ignorato la domanda principale in grassetto poiché altri avevano già risposto.
Stig Hemmer,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.