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::ssortingng toSsortingng() const; MyStruct2 my_struct_2; int an_int; };
L’erreur est:
erreur C2678: binary ‘==’: aucun opérateur trouvé prenant un opérande gauche de type ‘myproj :: MyStruct1’ (ou il n’y a pas de conversion acceptable)
Pourquoi?
En C ++, les struct
n’ont 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 l’ont dit, vous devez mettre en place une fonction de comparaison vous-même.
Il y a un moyen proposé de demander au compilateur de générer l’implémentation évidente / naïve (?): Voir ici .
Il peut sembler un peu inutile de C ++ de ne pas l’avoir déjà normalisé, mais souvent les structs / classes ont des membres de données à exclure de la comparaison (p. Ex. Compteurs, résultats mis en cache, capacité du conteneur, code succès / erreur de la dernière opération) en tant que décisions à prendre à propos d’une myriade de choses, y compris, mais sans s’y limiter:
int
particulier, peuvent éliminer 99% des objects inégaux très rapidement, alors qu’un membre map
peut souvent avoir des entrées identiques et être relativement coûteux à comparer. , le programmeur peut avoir des idées que le compilateur ne peut pas vector
, list
), et si oui, est-il correct de les sortinger avant de comparer ou d’utiliser de la mémoire supplémentaire pour sortinger les données temporaires? union
comparer operator==
eux-mêmes (mais peuvent avoir compare()
ou operator<
ou str()
ou getters ...) Donc, il est agréable d'avoir une erreur jusqu'à ce que vous ayez pensé explicitement à ce que la comparaison devrait signifier pour votre structure spécifique, plutôt que de la laisser comstackr sans vous donner un résultat significatif au moment de l'exécution .
Cela dit, ce serait bien si C ++ vous permettait de dire l' bool operator==() const = default;
quand vous aviez décidé qu'un test "naïf" membre par membre était correct. Même chose pour !=
. Étant donné les multiples membres / bases, les implémentations "default" <
, <=
, >
et >=
semblent sans espoir bien que - en cascade d’ordre de déclaration soit possible mais très improbable compte tenu des impératifs contradictoires nécessairement avant les membres, regroupés par accessibilité, construction / destruction avant utilisation dépendante). Pour être plus utile, le C ++ nécessiterait un nouveau système d'annotation membre / firebase database pour guider les choix - ce serait une bonne chose dans la norme, idéalement couplé à la génération de code définie par l'utilisateur basée sur AST ... ça va arriver un jour.
Une implémentation raisonnable et efficace serait probablement:
inline bool operator==(const MyStruct1& lhs, const MyStruct1& rhs) { return lhs.my_struct2 == rhs.my_struct2 && lhs.an_int == rhs.an_int; }
Notez que cela nécessite également un operator==
pour MyStruct2
.
Les implications de cette implémentation et les alternatives sont discutées sous le titre Discussion des spécificités de votre MyStruct1 ci-dessous.
Il est facile d'exploiter les opérateurs de comparaison std::tuple
pour comparer vos propres instances de classe - utilisez simplement std::tie
pour créer des tuples de références à des champs dans l'ordre de comparaison souhaité. Générer mon exemple 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.-à-d. Pouvez éditer un facteur avec les librairies d'entreprise et tierces) la classe que vous voulez comparer, et en particulier avec la préparation de C ++ 14 à déduire le type de retour de fonction de l'instruction return
, "associer" la fonction membre à la classe que vous souhaitez pouvoir comparer:
auto tie() const { return std::tie(my_struct1, an_int); }
Ensuite, les comparaisons ci-dessus simplifient:
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 des opérateurs de boost (recherchez less_than_comparable
). Si cela ne convient pas pour une raison quelconque, vous pouvez aimer ou non l'idée de 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 ensuite être utilisé a la ...
#define MY_STRUCT_FIELDS(X) X.my_struct2, X.an_int TIED_COMPARISONS(MyStruct1, MY_STRUCT_FIELDS)
(C ++ 14 version membre-cravate ici )
Il y a des implications dans le choix de fournir un operator==()
autonome ou membre operator==()
...
Implantation libre
Vous avez une décision intéressante à prendre. Comme votre classe peut être implicitement construite à partir d'un MyStruct2
, un bool operator==(const MyStruct2& lhs, const MyStruct2& rhs)
charge ...
my_MyStruct2 == my_MyStruct1
... en créant d'abord un MyStruct1
temporaire à MyStruct1
de my_myStruct2
, puis en effectuant la comparaison. Cela laisserait définitivement MyStruct1::an_int
défini sur la valeur par défaut du constructeur, à savoir -1
. Selon que vous incluez ou an_int
comparaison_int dans l'implémentation de votre operator==
, un MyStruct1
peut ou non être comparable à un MyStruct2
qui est lui-même my_struct_2
membre my_struct_2
! De plus, créer un MyStruct1
temporaire peut être une opération très inefficace, car il implique de copier le membre my_struct2
existant dans un my_struct2
temporaire, puis de le jeter après la comparaison. (Bien sûr, vous pouvez empêcher cette construction implicite de MyStruct1
des MyStruct1
de comparaison en rendant ce constructeur explicit
ou en supprimant la valeur par défaut pour an_int
.)
Mise en place des membres
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
, nécessaire uniquement pour l'implémentation du membre, indique au comparateur que la comparaison d'objects ne les modifie pas. Il peut donc être autorisé sur des objects const
.
Parfois, le moyen le plus simple d'obtenir le type de comparaison que vous souhaitez peut être ...
return lhs.to_ssortingng() == rhs.to_ssortingng();
... ce qui est souvent très coûteux aussi - ces ssortingng
créées péniblement pour être jetées! 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 dans laquelle les valeurs presque égales sont traitées comme égales pendant 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 objects.
La comparaison ne fonctionne pas sur les structures en C ou C ++. Comparez plutôt par champs.
Par défaut, les structures n’ont pas d’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 == pour 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.