En comparant deux instances de la structure suivante, je reçois une erreur:
struct MyStruct1 {
Position(const MyStruct2 &_my_struct_2, const int _an_int = -1) :
my_struct_2(_my_struct_2),
an_int(_an_int)
{}
std::string toString() const;
MyStruct2 my_struct_2;
int an_int;
};
L'erreur est:
erreur C2678: binaire '==': aucun opérateur trouvé ne prend d'opérande gauche du type 'myproj :: MyStruct1' (ou aucune conversion acceptable)
Pourquoi?
En C++, struct
s n'a pas d'opérateur de comparaison généré par défaut. Vous devez écrire le vôtre:
bool operator==(const MyStruct1& lhs, const MyStruct1& rhs)
{
return /* your comparison code goes here */
}
Comme d'autres personnes l'ont dit, vous devez implémenter vous-même une fonction de comparaison.
Il existe un moyen proposé de demander au compilateur de générer l'implémentation évidente/naïve (?): Voir ici .
Cela peut sembler un peu inutile du C++ de ne pas l'avoir déjà normalisé, mais souvent les structures/classes ont des membres de données à exclure de la comparaison (par exemple des compteurs, résultats en cache, capacité du conteneur, succès de la dernière opération/code d'erreur, curseurs), ainsi que les décisions à prendre sur une myriade de choses, notamment:
int
particulier peut éliminer très rapidement 99% des objets inégaux, alors qu'un membre map<string,string>
peut souvent avoir des entrées identiques et être relativement coûteux à comparer - si les valeurs sont chargées à l'exécution, programmeur peut avoir des idées le compilateur ne peut pas éventuellementvector
, list
) et, dans l'affirmative, s'il est correct de les trier sur place avant de comparer ou d'utiliser de la mémoire supplémentaire pour trier les temporaires à chaque comparaison est faitunion
compareroperator==
eux-mêmes (mais peuvent avoir compare()
ou operator<
ou str()
ou des getters ...)Donc, c'est un peu agréable d'avoir une erreur jusqu'à ce que vous ayez explicitement réfléchi à ce que la comparaison devrait signifier pour votre structure spécifique, plutôt que de le laisser compiler mais ne pas vous donner un résultat significatif au moment de l'exécution .
Cela dit, il serait bon que C++ vous laisse dire bool operator==() const = default;
lorsque vous auriez décidé de procéder à un test "naïf" membre par membre ==
était ok. Même chose pour !=
. Avec plusieurs membres/bases, les implémentations "par défaut" <
, <=
, >
Et >=
Semblent sans espoir - cascadées sur la base de l'ordre de déclaration possible mais très peu probable d'être ce que l'on souhaite, étant donné les impératifs contradictoires en matière de commande des membres (les bases étant nécessairement avant les membres, le regroupement par accessibilité, la construction/destruction avant une utilisation dépendante). Pour être plus utile, le C++ aurait besoin d’un nouveau système d’annotation base de données/données pour guider les choix - ce serait un atout majeur dans la norme mais, idéalement, associé à une génération de code définie par l’utilisateur basée sur AST ... Je pense ça va arriver un jour.
C'est probable qu'une implémentation raisonnable et efficace serait:
inline bool operator==(const MyStruct1& lhs, const MyStruct1& rhs)
{
return lhs.my_struct2 == rhs.my_struct2 &&
lhs.an_int == rhs.an_int;
}
Notez que ceci nécessite également un operator==
Pour MyStruct2
.
Les implications de cette implémentation et des alternatives sont discutées sous la rubrique Discussion des spécificités de votre MyStruct1 ci-dessous.
Il est facile de tirer parti des opérateurs de comparaison de std::Tuple
Pour comparer vos propres instances de classe. Il vous suffit d'utiliser std::tie
Pour créer des n-uplets de références à des champs dans l'ordre de comparaison souhaité. Généraliser mon exemple de ici :
inline bool operator==(const MyStruct1& lhs, const MyStruct1& rhs)
{
return std::tie(lhs.my_struct2, lhs.an_int) ==
std::tie(rhs.my_struct2, rhs.an_int);
}
inline bool operator<(const MyStruct1& lhs, const MyStruct1& rhs)
{
return std::tie(lhs.my_struct2, lhs.an_int) <
std::tie(rhs.my_struct2, rhs.an_int);
}
// ...etc...
Lorsque vous "possédez" (c'est-à-dire que vous pouvez éditer, un facteur avec les bibliothèques d'entreprise et tierces) la classe que vous souhaitez comparer, et en particulier avec la capacité de C++ 14 à déduire le type de retour de fonction de l'instruction return
, c'est Il est souvent plus agréable d’ajouter une fonction membre "tie" à la classe que vous voulez pouvoir comparer:
auto tie() const { return std::tie(my_struct1, an_int); }
Ensuite, les comparaisons ci-dessus se simplifient pour:
inline bool operator==(const MyStruct1& lhs, const MyStruct1& rhs)
{
return lhs.tie() == rhs.tie();
}
Si vous voulez un ensemble plus complet d'opérateurs de comparaison, je suggère opérateurs de boost (recherchez less_than_comparable
). Si cela ne vous convient pas pour une raison quelconque, vous pouvez ou non aimer l’idée des macros de support (en ligne) :
#define TIED_OP(STRUCT, OP, GET_FIELDS) \
inline bool operator OP(const STRUCT& lhs, const STRUCT& rhs) \
{ \
return std::tie(GET_FIELDS(lhs)) OP std::tie(GET_FIELDS(rhs)); \
}
#define TIED_COMPARISONS(STRUCT, GET_FIELDS) \
TIED_OP(STRUCT, ==, GET_FIELDS) \
TIED_OP(STRUCT, !=, GET_FIELDS) \
TIED_OP(STRUCT, <, GET_FIELDS) \
TIED_OP(STRUCT, <=, GET_FIELDS) \
TIED_OP(STRUCT, >=, GET_FIELDS) \
TIED_OP(STRUCT, >, GET_FIELDS)
... qui peut alors être utilisé à la ...
#define MY_STRUCT_FIELDS(X) X.my_struct2, X.an_int
TIED_COMPARISONS(MyStruct1, MY_STRUCT_FIELDS)
(Version cravate de membre C++ 14 ici )
Il y a des implications pour le choix de fournir un nom indépendant par rapport à un membre operator==()
...
Implémentation autonome
Vous avez une décision intéressante à prendre. Comme votre classe peut être construite de manière implicite à partir de MyStruct2
, Une fonction autonome/non membre bool operator==(const MyStruct2& lhs, const MyStruct2& rhs)
prendrait en charge ...
my_MyStruct2 == my_MyStruct1
... en créant d'abord un MyStruct1
temporaire à partir de my_myStruct2
, puis en effectuant la comparaison. Ceci laisserait définitivement MyStruct1::an_int
Défini sur la valeur de paramètre par défaut du constructeur de -1
. Selon que vous incluez la comparaison an_int
Dans la mise en œuvre de votre operator==
, Un MyStruct1
Peut être égal ou non à un MyStruct2
Qui se compare lui-même à le membre MyStruct1
my_struct_2
! De plus, créer un MyStruct1
Temporaire peut être une opération très inefficace, car cela implique de copier le membre my_struct2
Existant dans un temporaire, uniquement pour le jeter après la comparaison. (Bien entendu, vous pouvez éviter cette construction implicite de MyStruct1
À des fins de comparaison en rendant ce constructeur explicit
ou en supprimant la valeur par défaut de an_int
.)
Implémentation du membre
Si vous voulez éviter la construction implicite d'un MyStruct1
À partir d'un MyStruct2
, Faites de l'opérateur de comparaison une fonction membre:
struct MyStruct1
{
...
bool operator==(const MyStruct1& rhs) const
{
return tie() == rhs.tie(); // or another approach as above
}
};
Notez que le mot clé const
(uniquement nécessaire pour l'implémentation de membre) indique au compilateur que la comparaison d'objets ne les modifie pas et peut donc être autorisée sur les objets const
.
Parfois, le moyen le plus simple d'obtenir le type de comparaison que vous souhaitez peut être ...
return lhs.to_string() == rhs.to_string();
... qui coûte souvent très cher - ces string
s sont cruellement créés pour être jetés! Pour les types avec des valeurs à virgule flottante, la comparaison des représentations visibles signifie que le nombre de chiffres affichés détermine la tolérance à l'intérieur de laquelle les valeurs presque égales sont traitées comme étant égales lors de la comparaison.
Vous devez définir explicitement operator ==
pour MyStruct1
.
struct MyStruct1 {
bool operator == (const MyStruct1 &rhs) const
{ /* your logic for comparision between "*this" and "rhs" */ }
};
Maintenant, la comparaison == est légale pour 2 de ces objets.
La comparaison ne fonctionne pas sur les structures en C ou C++. Comparez par champs à la place.
À partir de C++ 20, il devrait être possible d’ajouter un ensemble complet d’opérateurs de comparaison par défaut (==
, <=
, etc.) à une classe en déclarant un opérateur de comparaison à trois voies par défaut (opérateur "vaisseau spatial"), comme ceci:
struct Point {
int x;
int y;
auto operator<=>(const Point&) const = default;
};
Avec un compilateur C++ 20 conforme, l'ajout de cette ligne à MyStruct1 et MyStruct2 peut suffire à permettre des comparaisons d'égalité, en supposant que la définition de MyStruct2 soit compatible.
Par défaut, les structures n'ont pas de ==
_ opérateur. Vous devrez écrire votre propre implémentation:
bool MyStruct1::operator==(const MyStruct1 &other) const {
... // Compare the values, and return a bool result.
}
Hors de la boîte, l'opérateur == ne fonctionne que pour les primitives. Pour que votre code fonctionne, vous devez surcharger l'opérateur == de votre structure.
Parce que vous n'avez pas écrit d'opérateur de comparaison pour votre structure. Le compilateur ne le génère pas pour vous, donc si vous voulez une comparaison, vous devez l'écrire vous-même.