Default comparisons (since C++20)
Les fonctions d'opérateur de comparaison peuvent être explicitement définies par défaut pour demander au compilateur de générer la comparaison par défaut correspondante pour une classe.
Table des matières |
Définition
Une
fonction opérateur de comparaison par défaut
est une fonction opérateur de comparaison non template (c'est-à-dire
<=>
,
==
,
!=
,
<
,
>
,
<=
, ou
>=
) satisfaisant toutes les conditions suivantes :
-
Il s'agit d'une
fonction membre non-statique
ou d'une
fonction amie
d'une classe
C. -
Elle est
définie comme par défaut
dans
Cou dans un contexte oùCest complète . -
Elle possède deux paramètres de type
const
C
&
ou deux paramètres de type
C, où le paramètre objet implicite (le cas échéant) est considéré comme le premier paramètre.
Une telle fonction d'opérateur de comparaison est appelée une
fonction d'opérateur de comparaison par défaut pour la classe
C
.
struct X { bool operator==(const X&) const = default; // Correct bool operator==(const X&) = default; // Erreur : le type du paramètre objet implicite // est X& bool operator==(this X, X) = default; // Correct }; struct Y { friend bool operator==(Y, Y) = default; // Correct friend bool operator==(Y, const Y&) = default; // Erreur : types de paramètres différents }; bool operator==(const Y&, const Y&) = default; // Erreur : pas un ami de Y
Les recherches de nom et les vérifications d'accès dans la définition implicite d'une fonction d'opérateur de comparaison sont effectuées depuis un contexte équivalent à son corps de fonction. Une définition d'une fonction d'opérateur de comparaison comme étant par défaut qui apparaît dans une classe doit être la première déclaration de cette fonction.
Ordre de comparaison par défaut
Étant donné une classe
C
, une liste de sous-objets est formée par les sous-objets suivants dans l'ordre :
-
Les sous-objets de classe de base directs de
C, dans l'ordre de déclaration. -
Les
data members
non statiques de
C, dans l'ordre de déclaration.
-
- Si un quelconque sous-objet membre est de type tableau, il est étendu en la séquence de ses éléments, dans l'ordre des indices croissants. L'extension est récursive : les éléments de tableau de types tableau seront à nouveau étendus jusqu'à ce qu'il n'y ait plus de sous-objet de type tableau.
Pour tout objet
x
de type
C
, dans les descriptions suivantes :
- Soit n le nombre de sous-objets dans la liste (étendue) de sous-objets pour x .
- Soit x_i le i -ième sous-objet dans la liste (étendue) de sous-objets pour x , où x_i est formé par une séquence de conversions dérivé-vers-base , expressions d'accès aux membres de classe , et expressions d'indice de tableau appliquées à x .
struct S {}; struct T : S { int arr[2][2]; } t; // La liste des sous-objets pour « t » comprend les 5 sous-objets suivants dans l'ordre : // (S)t → t[0][0] → t[0][1] → t[1][0] → t[1][1]
Comparaison à trois voies
Un opérateur <=> pour un type classe peut être défini comme par défaut avec n'importe quel type de retour.
Types de catégories de comparaison
Il existe trois types de catégories de comparaison :
| Type | Les valeurs équivalentes sont.. | Les valeurs incomparables sont.. |
|---|---|---|
| std::strong_ordering | indiscernables | non autorisées |
| std::weak_ordering | discernables | non autorisées |
| std::partial_ordering | discernables | autorisées |
Comparaison à trois voies synthétisée
La
comparaison triple synthétisée
du type
T
entre les glvalues
a
et
b
de même type est définie comme suit :
-
Si la résolution de surcharge pour
a
<=>
b
aboutit à un candidat utilisable, et peut être explicitement converti en
Ten utilisantstatic_cast, la comparaison synthétisée est static_cast < T > ( a <=> b ) . - Sinon, si l'une des conditions suivantes est satisfaite, la comparaison synthétisée n'est pas définie :
-
- La résolution de surcharge pour a <=> b trouve au moins un candidat viable.
-
Tn'est pas un type de catégorie de comparaison. - La résolution de surcharge pour a == b ne résulte pas en un candidat utilisable.
- La résolution de surcharge pour a < b ne résulte pas en un candidat utilisable.
-
Sinon, si
Test std::strong_ordering , la comparaison synthétisée est
a == b ? std::strong_ordering::equal : a < b ? std::strong_ordering::less : std::strong_ordering::greater
` et contient des termes spécifiques au C++ qui doivent être préservés. Seul le texte environnant (comme ce commentaire) a été traduit en français.
-
Sinon, si
Test std::weak_ordering , la comparaison synthétisée est
a == b ? std::weak_ordering::equivalent : a < b ? std::weak_ordering::less : std::weak_ordering::greater
-
Sinon (
Test std::partial_ordering ), la comparaison synthétisée est
a == b ? std::partial_ordering::equivalent : a < b ? std::partial_ordering::less : b < a ? std::partial_ordering::greater : std::partial_ordering::unordered
` et contient des termes spécifiques au C++ qui doivent être préservés. Seul le texte environnant (s'il y en avait) aurait été traduit en français.
Type de retour générique
Si le type de retour déclaré d'une fonction opérateur de comparaison à trois voies par défaut (
operator
<=>
) pour un type de classe
C
est
auto
, le type de retour est déduit des types de retour des comparaisons à trois voies entre les sous-objets correspondants d'un objet
x
de type
C
.
Pour chaque sous-objet x_i dans la liste des sous-objets (étendue) pour x :
- Effectuer la résolution de surcharge pour x_i <=> x_i , si la résolution de surcharge ne produit pas un candidat utilisable, l'opérateur operator <=> par défaut est défini comme supprimé.
-
Noter la version sans qualificatifs cv du type de
x_i
<=>
x_i
comme
R_i, siR_in'est pas un type de catégorie de comparaison, l'opérateur operator <=> par défaut est défini comme supprimé.
Si l'opérateur operator <=> par défaut n'est pas défini comme supprimé, son type de retour est déduit comme std:: common_comparison_category_t < R_1, R_2, ..., R_n > .
Type de retour non-générique
Si le type de retour déclaré de l'opérateur operator <=> par défaut n'est pas auto , il ne peut contenir aucun type substitutif (par exemple decltype ( auto ) ).
S'il existe un sous-objet x_i dans la liste (étendue) des sous-objets pour x tel que la comparaison à trois voies synthétisée du type de retour déclaré entre x_i et x_i n'est pas définie, l'opérateur par défaut operator <=> est défini comme supprimé.
Résultat de comparaison
Soient x et y les paramètres d'un operator <=> par défaut, désignons chaque sous-objet dans la liste (développée) des sous-objets pour x et y comme x_i et y_i respectivement. La comparaison à trois voies par défaut entre x et y est effectuée en comparant les sous-objets correspondants x_i et y_i avec un ordre croissant de i .
Soit
R
le type de retour (éventuellement déduit), le résultat de la comparaison entre
x_i
et
y_i
est le résultat de la comparaison à trois voies synthétisée de type
R
entre
x_i
et
y_i
.
- Lors de la comparaison à trois voies par défaut entre x et y , si une comparaison sous-objet par sous-objet entre x_i et y_i génère un résultat v_i tel que la conversion contextuelle de v_i ! = 0 en bool donne true , la valeur de retour est une copie de v_i (les sous-objets restants ne seront pas comparés).
- Sinon, la valeur de retour est static_cast < R > ( std :: strong_ordering :: equal ) .
#include <compare> #include <iostream> #include <set> struct Point { int x; int y; auto operator<=>(const Point&) const = default; /* fonctions non-comparaison */ }; int main() { Point pt1{1, 1}, pt2{1, 2}; std::set<Point> s; // OK s.insert(pt1); // OK // les fonctions d'opérateur de comparaison bidirectionnelle ne sont pas requises d'être définies explicitement : // operator== est implicitement déclaré (voir ci-dessous) // les résolutions de surcharge des autres candidats sélectionneront les candidats réécrits std::cout << std::boolalpha << (pt1 == pt2) << ' ' // false << (pt1 != pt2) << ' ' // true << (pt1 < pt2) << ' ' // true << (pt1 <= pt2) << ' ' // true << (pt1 > pt2) << ' ' // false << (pt1 >= pt2) << ' '; // false }
Comparaison d'égalité
Déclaration explicite
Un operator == pour un type classe peut être défini comme étant par défaut avec le type de retour bool .
Étant donné une classe
C
et un objet
x
de type
C
, s'il existe un sous-objet
x_i
dans la liste (développée) des sous-objets pour
x
tel que la résolution de surcharge pour
x_i
==
x_i
ne résulte pas en un candidat utilisable, l'opérateur
operator
==
par défaut est défini comme supprimé.
Soient x et y les paramètres d'un operator == par défaut, désignons chaque sous-objet dans la liste (développée) de sous-objets pour x et y comme x_i et y_i respectivement. La comparaison d'égalité par défaut entre x et y est effectuée en comparant les sous-objets correspondants x_i et y_i dans l'ordre croissant de i .
Le résultat de la comparaison entre x_i et y_i est le résultat de x_i == y_i .
- Lors de la comparaison d'égalité par défaut entre x et y , si une comparaison sous-objet par sous-objet entre x_i et y_i génère un résultat v_i tel que la conversion contextuelle de v_i en bool donne false , la valeur de retour est false (les sous-objets restants ne seront pas comparés).
- Sinon, la valeur de retour est true .
#include <iostream> struct Point { int x; int y; bool operator==(const Point&) const = default; /* non-comparison functions */ }; int main() { Point pt1{3, 5}, pt2{2, 5}; std::cout << std::boolalpha << (pt1 != pt2) << '\n' // true << (pt1 == pt1) << '\n'; // true struct [[maybe_unused]] { int x{}, y{}; } p, q; // if (p == q) {} // Error: operator== is not defined }
Déclaration implicite
Si une classe
C
ne déclare explicitement aucun membre ou ami nommé
operator
==
, une fonction opérateur
==
est déclarée implicitement pour chaque
operator
<=>
défini comme étant par défaut. Chaque
operator
==
implicitement déclaré possède le même accès et la même
définition de fonction
et se trouve dans la même
portée de classe
que le
operator
<=>
par défaut respectif, avec les modifications suivantes :
- L' identifiant du déclarateur est remplacé par operator == .
- Le type de retour est remplacé par bool .
template<typename T> struct X { friend constexpr std::partial_ordering operator<=>(X, X) requires (sizeof(T) != 1) = default; // déclare implicitement : friend constexpr bool operator==(X, X) // requires (sizeof(T) != 1) = default; [[nodiscard]] virtual std::strong_ordering operator<=>(const X&) const = default; // déclare implicitement : [[nodiscard]] virtual bool // operator==(const X&) const = default; };
Comparaison secondaire
Une fonction d'opérateur de comparaison secondaire (
!=
,
<
,
>
,
<=
, ou
>=
) pour un type classe peut être définie comme étant par défaut avec le type de retour
bool
.
Soit
@
l'un des cinq opérateurs de comparaison secondaires, pour chaque
operator@
par défaut avec les paramètres
x
et
y
, jusqu'à deux résolutions de surcharge sont effectuées (sans considérer le
operator@
par défaut comme candidat) pour déterminer s'il est défini comme supprimé.
- La première résolution de surcharge est effectuée pour x @ y . Si la résolution de surcharge ne résulte pas en un candidat utilisable, ou si le candidat sélectionné n'est pas un candidat réécrit , l'opérateur par défaut operator@ est défini comme supprimé. Il n'y a pas de seconde résolution de surcharge dans ces cas.
- La seconde résolution de surcharge est effectuée pour le candidat réécrit sélectionné de x @ y . Si la résolution de surcharge ne résulte pas en un candidat utilisable, l'opérateur par défaut operator@ est défini comme supprimé.
Si x @ y ne peut pas être implicitement converti en bool , l'opérateur operator@ par défaut est défini comme supprimé.
Si l'opérateur operator@ par défaut n'est pas défini comme supprimé, il produit x @ y .
struct HasNoRelational {}; struct C { friend HasNoRelational operator<=>(const C&, const C&); bool operator<(const C&) const = default; // OK, la fonction est définie par défaut };
Mots-clés
Rapports de défauts
Les rapports de défauts modifiant le comportement suivants ont été appliqués rétroactivement aux normes C++ précédemment publiées.
| DR | Appliqué à | Comportement publié | Comportement corrigé |
|---|---|---|---|
| CWG 2539 | C++20 |
la comparaison à trois voies synthétisée choisirait
static_cast même si la conversion explicite n'est pas disponible |
ne choisit pas
static_cast dans ce cas |
| CWG 2546 | C++20 |
l'opérateur secondaire par défaut
operator@
n'était pas
défini comme supprimé si la résolution de surcharge de x @ y sélectionne un candidat réécrit non utilisable |
défini comme supprimé
dans ce cas |
| CWG 2547 | C++20 |
il n'était pas clair si les fonctions d'opérateur de comparaison
pour les non-classes peuvent être définies par défaut |
elles ne peuvent pas être définies par défaut |
| CWG 2568 | C++20 |
la définition implicite des fonctions d'opérateur de comparaison
pourrait violer les règles d'accès aux membres |
les vérifications d'accès sont effectuées
depuis un contexte équivalent à leurs corps de fonction |
Voir aussi
- résolution de surcharge dans un appel à un opérateur surchargé
- Opérateur de comparaison à trois voies intégré
- Surcharge d'opérateur pour les opérateurs de comparaison