Qu'est-ce que l'opérateur <=> en C ++?

214

Pendant que j'essayais d'en savoir plus sur les opérateurs C ++ , je suis tombé sur un étrange opérateur de comparaison sur cppreference.com , * dans un tableau qui ressemblait à ceci:

entrez la description de l'image ici

"Eh bien, si ce sont des opérateurs communs en C ++, je ferais mieux de les apprendre", pensai-je. Mais toutes mes tentatives pour élucider ce mystère ont échoué. Même ici, sur Stack Overflow, je n'ai pas eu de chance dans ma recherche.

Existe-t-il une connexion entre <=> et C ++ ?

Et s'il y en a un, que fait exactement cet opérateur?

* Entre-temps, cppreference.com a mis à jour cette page et contient maintenant des informations sur l' <=>opérateur.

qlp
la source
82
@haccks: Oh s'il vous plaît, nous avons eu beaucoup de questions sur des choses qui n'ont même pas été votées dans la norme. Nous avons une balise C ++ 20 pour une raison. Ce genre de choses est vraiment sur le sujet.
Nicol Bolas
1
@ cubuspl42 bar< foo::operator<=>est un exemple de la façon dont il pourrait ressembler à l' <--opérateur.
Yakk - Adam Nevraumont
8
@haccks: C'est vrai. Comme C ++ 11 est une balise sur les compilateurs qui implémentent C ++ 11. Et C ++ 14 est une balise sur les compilateurs qui implémentent C ++ 14. Et C ++ 17 concerne les compilateurs qui implémentent C ++ 17. Non, le C ++ 20 est la balise des informations sur C ++ 20. Et puisque cette question concerne C ++ 20, elle est là. Le tag wiki qui était faux, pas le tag lui-même.
Nicol Bolas

Réponses:

179

C'est ce qu'on appelle l' opérateur de comparaison à trois .

Selon la proposition de papier P0515 :

Il y a un nouvel opérateur de comparaison à trois voies, <=>. L'expression a <=> brenvoie un objet qui compare <0si a < b, compare >0si a > bet compare ==0si aet bsont égaux / équivalents.

Pour écrire toutes les comparaisons pour votre type, il suffit d'écrire operator<=>qui renvoie le type de catégorie approprié:

  • Retour un _ordering si votre type supports naturellement <, et nous générerons efficacement <, >, <=, >=, ==et !=; sinon retournez une _equality , et nous générerons efficacement == et ! = .

  • Renvoie fort si pour votre type a == bimplique f(a) == f(b)(substituabilité, où f lit uniquement l'état de comparaison-saillant accessible en utilisant l'interface const non privée), sinon retourne faible.

La référence cpp dit:

Les expressions d'opérateur de comparaison à trois ont la forme

lhs <=> rhs   (1)  

L'expression renvoie un objet qui

  • compare <0silhs < rhs
  • compare >0silhs > rhs
  • et compare ==0si lhset rhssont égaux / équivalents.
msc
la source
93
Pour ceux qui sont confus (comme moi) sur ce que signifie "compare <0", "compare >0" et "compare ==0", ils signifient les <=>retours une valeur négative, positive ou nulle, selon les arguments. Tout comme strncmpet memcmp.
Cornstalks
1
@Dai même si les deux 'a' < 'a'et 'c' < 'a'sont à la fois faux, 'a' < 'a'et 'a' < 'c'ne sont pas. EN ordre fort, ce qui suit est vrai: a != ba < b || b < a
Revolver_Ocelot
1
@Revolver_Ocelot Ah, donc il peut être défini / généré comme operator==(T x, T y) { return !(x < y) && !(y < x); }et operator!=(T x, T y) { return (x < y) || (y < x); }- ah-ha! Bien sûr, cela est moins efficace qu'un vrai ==car il invoque la comparaison deux fois, mais toujours soigné.
Dai
3
Que signifient «retour fort» et «retour faible»?
lucidbrot
2
@hkBattousai signifie que l'objet retourne, lorsqu'il est comparé < 0à true. Autrement dit, si a < balors (a <=> b) < 0est toujours vrai.
rmobis
116

Le 11/11/2017 , le comité ISO C ++ a adopté la proposition de Herb Sutter pour l' opérateur de comparaison à trois voies <=> "vaisseau spatial" comme l'une des nouvelles fonctionnalités ajoutées au C ++ 20 . Dans l'article intitulé Comparaison cohérente Sutter, Maurer et Brown démontrent les concepts du nouveau design. Pour un aperçu de la proposition, voici un extrait de l'article:

L'expression a <=> b renvoie un objet qui compare <0 si a <b , compare > 0 si a> b et compare == 0 si a et b sont égaux / équivalents.

Cas courant : pour écrire toutes les comparaisons de votre type X avec le type Y , avec la sémantique des membres, écrivez simplement:

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

Cas avancés: pour écrire toutes les comparaisons de votre type X avec le type Y , il suffit d'écrire l' opérateur <=> qui prend un Y , peut utiliser = default pour obtenir la sémantique des membres si vous le souhaitez et retourne le type de catégorie approprié:

  • Retour un _ordering si votre type supports naturellement < , et nous allons générer efficacement symétrique < , > , <= , > = , == et ! = ; sinon retournez une _equality , et nous générerons efficacement symétrique == et ! = .
  • Renvoie strong_ si pour votre type, a == b implique f (a) == f (b) (substituabilité, où f ne lit que l'état de comparaison qui est accessible à l'aide des membres const publics ), sinon retour faible_ .

Catégories de comparaison

Cinq catégories de comparaison sont définies en tant que std::types, chacune ayant les valeurs prédéfinies suivantes:

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

Les conversions implicites entre ces types sont définies comme suit:

  • strong_orderingavec des valeurs { less, equal, greater} convertit implicitement:
    • weak_orderingavec des valeurs { less, equivalent, greater}
    • partial_orderingavec des valeurs { less, equivalent, greater}
    • strong_equalityavec des valeurs { unequal, equal, unequal}
    • weak_equalityavec des valeurs { nonequivalent, equivalent, nonequivalent}
  • weak_orderingavec des valeurs { less, equivalent, greater} convertit implicitement:
    • partial_orderingavec des valeurs { less, equivalent, greater}
    • weak_equalityavec des valeurs { nonequivalent, equivalent, nonequivalent}
  • partial_orderingavec des valeurs { less, equivalent, greater, unordered} convertit implicitement:
    • weak_equalityavec des valeurs { nonequivalent, equivalent, nonequivalent, nonequivalent}
  • strong_equalityavec des valeurs { equal, unequal} convertit implicitement en:
    • weak_equalityavec des valeurs { equivalent, nonequivalent}

Comparaison à trois

Le <=>jeton est introduit. La séquence de caractères est <=>symbolisée par <= >, dans l'ancien code source. Par exemple, X<&Y::operator<=>doit ajouter un espace pour conserver sa signification.

L'opérateur surchargeable <=>est une fonction de comparaison à trois voies et a une priorité supérieure <et inférieure à <<. Il renvoie un type qui peut être comparé à un littéral, 0mais d'autres types de retour sont autorisés, tels que les modèles d'expression. Tous les <=>opérateurs définis dans la langue et dans la bibliothèque standard renvoient l'un des 5 std::types de catégories de comparaison susmentionnés .

Pour les types de langue, les <=>comparaisons intégrées de même type suivantes sont fournies. Tous sont constexpr , sauf indication contraire. Ces comparaisons ne peuvent pas être invoquées de manière hétérogène à l'aide de promotions / conversions scalaires.

  • Pour les booltypes, intégral et pointeur, <=>renvoie strong_ordering.
  • Pour les types de pointeurs, les différentes qualifications cv et les conversions dérivées en base sont autorisées à invoquer un intégré homogène <=>, et il existe des hétérogènes intégrés operator<=>(T*, nullptr_t). Seules les comparaisons de pointeurs vers le même objet / allocation sont des expressions constantes.
  • Pour les types à virgule flottante fondamentaux, <=>renvoie partial_orderinget peut être invoqué de manière hétérogène en élargissant les arguments à un type à virgule flottante plus grand.
  • Pour les énumérations, <=>renvoie la même chose que le type sous-jacent de l'énumération <=>.
  • Car nullptr_t, <=>retourne strong_orderinget rapporte toujours equal.
  • Pour les tableaux copiables, T[N] <=> T[N]renvoie le même type que T's <=>et effectue une comparaison lexicographique élément par élément. Il n'y en a pas <=>pour les autres tableaux.
  • Car voidil n'y en a pas <=>.

Pour mieux comprendre le fonctionnement interne de cet opérateur, veuillez lire le document original . C'est exactement ce que j'ai découvert en utilisant les moteurs de recherche.

qlp
la source
1
Comme si cpp n'était pas déjà assez complexe. Pourquoi ne pas simplement écrire une méthode de comparaison ...
Leandro
6
@Leandro L'opérateur de vaisseau spatial est cette méthode de comparaison. En outre, il fonctionne et écrit (ou supprime) les six autres opérateurs de comparaison. Je prendrai une fonction d'opérateur de comparaison écrite sur six plaques chauffantes individuelles.
anonyme
Notez que les _equalitytypes sont morts: il s'est avéré que cela fonctionne <=>bien avec les quatre opérateurs relationnels mais pas aussi bien avec les deux opérateurs d'égalité (bien qu'il y ait du sucre syntaxique intense pour soutenir le cas commun où vous voulez tous).
Davis Herring
12

Cette réponse est devenue sans objet depuis que la page Web référencée a changé

La page Web à laquelle vous faites référence a été endommagée. Il était beaucoup édité ce jour-là et les différentes parties n'étaient pas synchronisées. Le statut quand je le regardais était:

En haut de la page, il répertorie les opérateurs de comparaison existants (en C ++ 14). Il n'y en a pas <=>.

Au bas de la page, ils auraient dû lister les mêmes opérateurs, mais ils se sont trompés et ont ajouté cette future suggestion.

gccne sait pas <=>encore (et avec -std=c++14, ne le sera jamais), donc il pense que vous vouliez dire a <= > b. C'est ce qui explique le message d'erreur.

Si vous essayez la même chose dans cinq ans, vous obtiendrez probablement un meilleur message d'erreur, quelque chose comme <=> not part of C++14.

Stig Hemmer
la source
1
La page Web vers laquelle OP est lié est correcte, tout comme la page à laquelle vous vous connectez. Il qualifie l' <=>opérateur avec le label (depuis C ++ 20), vous indiquant dans quelle version de la norme l'attendre. L'étiquetage des normes est une convention que cppreference.com suit. Bien sûr, vous n'avez pas de compilateur qui est revenu dans une machine à remonter le temps pour le prendre en charge, mais cpprefernce vous dit (correctement) à quoi vous attendre.
Spencer
Oui, mais ... Pas une réponse. Vous commentez ... ou quelque chose.
qlp
2
J'avais l'intention de créer un lien vers la même page Web que la question, mais j'ai raté. Je pense avoir répondu aux parties de la question auxquelles les autres réponses n'ont pas répondu. J'ai ignoré la principale question en gras, car d'autres y avaient déjà répondu.
Stig Hemmer