2024-01-08 13:10:00
La plupart des développeurs C++ sont habitués à définir ou à utiliser l’opérateur de comparaison à trois voies =default
peut être demandé par le compilateur. Ce qui est probablement moins connu, c’est que l’opérateur d’égalité peut également être défini ou demandé en C++20 ?
Publicité
Avant de discuter de l’opérateur d’égalité généré automatiquement, je voudrais revenir sur les faits clés concernant l’opérateur de comparaison à trois.
Rainer Grimm travaille depuis de nombreuses années en tant qu’architecte logiciel, responsable d’équipe et de formation. Il aime écrire des articles sur les langages de programmation C++, Python et Haskell, mais aime également intervenir fréquemment lors de conférences spécialisées. Sur son blog Modern C++, il parle intensément de sa passion C++.
L’opérateur de comparaison à trois voies
L’opérateur de comparaison à trois voies peut être défini ou avec =default
demande du compilateur. Dans les deux cas, vous obtenez les six opérateurs de comparaison : ==, !=, <, <=, >,
et >=
.
// threeWayComparison.cpp
#include
#include
struct MyInt {
int value;
explicit MyInt(int val): value{val} { }
auto operator<=>(const MyInt& rhs) const { // (1)
return value <=> rhs.value;
}
};
struct MyDouble {
double value;
explicit constexpr MyDouble(double val): value{val} { }
auto operator<=>(const MyDouble&) const = default; // (2)
};
template
constexpr bool isLessThan(const T& lhs, const T& rhs) {
return lhs < rhs;
}
int main() {
std::cout << std::boolalpha << std::endl;
MyInt myInt1(2011);
MyInt myInt2(2014);
std::cout << "isLessThan(myInt1, myInt2): "
<< isLessThan(myInt1, myInt2) << std::endl;
MyDouble myDouble1(2011);
MyDouble myDouble2(2014);
std::cout << "isLessThan(myDouble1, myDouble2): "
<< isLessThan(myDouble1, myDouble2) << std::endl;
std::cout << std::endl;
}
Les opérateurs de comparaison à trois voies définis par l'utilisateur (1) et générés par le compilateur (2) fonctionnent comme prévu.
Il existe cependant quelques différences notables entre les deux opérateurs de comparaison à trois facteurs. Le compilateur a déduit le type de retour pour MyInt
(1) prend en charge un ordre strict, le compilateur déduit le type de retour pour MyDouble
(2), cependant, ne prend en charge que la commande partielle. Les nombres à virgule flottante ne peuvent prendre en charge qu'un ordre partiel car des valeurs telles que NaN (Pas un numéro) ne peut pas être organisé. Par exemple, cela s'applique NaN == NaN
à false
évalué.
L'opérateur de comparaison à trois voies généré par le compilateur, qui est implicite constexpr
et noexcept
est, nécessite l'en-tête
Il effectue également une comparaison lexicographique. Dans ce contexte, la comparaison lexicographique signifie que toutes les classes de base sont comparées de gauche à droite et que tous les membres non statiques sont comparés dans l'ordre dans lequel ils ont été déclarés.
Disons que j'ajoute les deux classes MyInt
et MyDouble
un std::unordered_set
ajoutée.
struct MyInt {
int value;
std::unordered_set mySet;
explicit MyInt(int val): value{val}, mySet{val} { }
bool operator<=>(const MyInt& rhs) const {
if (auto first = value <=> rhs.value; first != 0) return first;
else return mySet <=> rhs.mySet;
}
};
struct MyDouble {
double value;
std::unordered_set mySet;
explicit MyDouble(double val): value{val}, mySet{val} { }
bool operator<=>(const MyDouble&) const = default;
};
La demande ou la définition de la comparaison à trois échoue car std::unordered_set
aucune commande prise en charge. std::unordered_set
ne prend en charge que les comparaisons d'égalité, et cela s'applique également à MyInt
et MyDouble
.
opérateur d'égalité
L'opérateur d'égalité est-il défini ou utilisé par le compilateur =default
demandé, vous obtenez automatiquement les opérateurs d’égalité et d’inégalité : ==
et !=
.
// equalityOperator.cpp
#include
#include
#include
struct MyInt {
int value;
std::unordered_set mySet;
explicit MyInt(int val): value{val}, mySet{val} { }
bool operator==(const MyInt& rhs) const {
return std::tie(value, mySet) == std::tie(rhs.value, rhs.mySet);
}
};
struct MyDouble {
double value;
std::unordered_set mySet;
explicit MyDouble(double val): value{val}, mySet{val} { }
bool operator==(const MyDouble&) const = default;
};
template
constexpr bool areEqual(const T& lhs, const T& rhs) {
return lhs == rhs;
}
template
constexpr bool areNotEqual(const T& lhs, const T& rhs) {
return lhs != rhs;
}
int main() {
std::cout << std::boolalpha << 'n';
MyInt myInt1(2011);
MyInt myInt2(2014);
std::cout << "areEqual(myInt1, myInt2): "
<< areEqual(myInt1, myInt2) << 'n';
std::cout << "areNotEqual(myInt1, myInt2): "
<< areNotEqual(myInt1, myInt2) << 'n';
std::cout << 'n';
MyDouble myDouble1(2011.0);
MyDouble myDouble2(2014.0);
std::cout << "areEqual(myDouble1, myDouble2): "
<< areEqual(myDouble1, myDouble2) << 'n';
std::cout << "areNotEqual(myDouble1, myDouble2): "
<< areNotEqual(myDouble1, myDouble2) << 'n';
std::cout << 'n';
}
Maintenant je peux MyInt
et MyDouble
comparer l’égalité et l’inégalité.
Au programme equalityOperator.cpp
J'ai utilisé une astuce – qui la reconnaîtra ?
Dans l'exemple ci-dessous, j'ai l'opérateur d'égalité de MyInt
implémenté en utilisant les opérateurs d'égalité de value
et mySet
enchaîné.
struct MyInt {
int value;
std::unordered_set mySet;
explicit MyInt(int val): value{val}, mySet{val} { }
bool operator==(const MyInt& rhs) const {
if (auto first = value == rhs.value; first != 0) return first;
else return mySet == rhs.mySet;
}
};
Ceci est assez sujet aux erreurs et semble moche si vous avez une classe avec plusieurs membres.
En revanche, j'ai std::tie
utilisé l'opérateur d'égalité dans le programme equalityOperator.cpp
implémenter.
struct MyInt {
int value;
std::unordered_set mySet;
explicit MyInt(int val): value{val}, mySet{val} { }
bool operator==(const MyInt& rhs) const {
return std::tie(value, mySet) == std::tie(rhs.value, rhs.mySet);
}
};
std::tie
crée un tuple de lvalue
-Références à ses arguments. Enfin, les tuples générés sont comparés lexicographiquement.
Et après?
Dans mon prochain article, je continuerai mon voyage à travers le C++20 et au-delà std::span
écrire. std::span
représente un objet qui fait référence à une séquence connectée d'objets.
(carte)
#Langage #programmation #lopérateur #dégalité #généré #automatiquement
1704839811