Je suis un grand fan de laisser le compilateur faire autant de travail que possible pour vous. Lors de l'écriture d'une classe simple, le compilateur peut vous donner les éléments suivants gratuitement:
- Un constructeur par défaut (vide)
- Un constructeur de copie
- Un destructeur
- Un opérateur d'affectation (
operator=
)
Mais il ne semble pas vous donner d'opérateurs de comparaison - comme operator==
ou operator!=
. Par exemple:
class foo
{
public:
std::string str_;
int n_;
};
foo f1; // Works
foo f2(f1); // Works
foo f3;
f3 = f2; // Works
if (f3 == f2) // Fails
{ }
if (f3 != f2) // Fails
{ }
Y a-t-il une bonne raison à cela? Pourquoi effectuer une comparaison membre par membre serait un problème? Évidemment, si la classe alloue de la mémoire, vous voudrez être prudent, mais pour une classe simple, le compilateur pourrait sûrement le faire pour vous?
==
, de la même manière qu'il y a une affectation automatique par défaut (=
) sous certaines conditions. (L'argument sur les pointeurs est incohérent car la logique s'applique à la fois pour=
et==
, et pas seulement pour le second).Réponses:
Le compilateur ne saurait pas si vous vouliez une comparaison de pointeurs ou une comparaison approfondie (interne).
Il est plus sûr de ne pas l'implémenter et de laisser le programmeur le faire lui-même. Ensuite, ils peuvent faire toutes les hypothèses qu'ils aiment.
la source
operator=
) fonctionnent généralement dans le même contexte que les opérateurs de comparaison - c'est-à-dire que l'on s'attend à ce que, après avoir effectuéa = b
, celaa == b
soit vrai. Il est certainement logique que le compilateur fournisse une valeuroperator==
par défaut en utilisant la même sémantique de valeur agrégée que pouroperator=
. Je soupçonne que paercebal a en fait raison ici en ce queoperator=
(et copie ctor) sont fournis uniquement pour la compatibilité C, et ils ne voulaient pas aggraver la situation.L'argument selon lequel si le compilateur peut fournir un constructeur de copie par défaut, il devrait être en mesure de fournir un défaut similaire a
operator==()
un certain sens. Je pense que la raison de la décision de ne pas fournir un défaut généré par le compilateur pour cet opérateur peut être devinée par ce que Stroustrup a dit à propos du constructeur de copie par défaut dans "La conception et l'évolution de C ++" (Section 11.4.1 - Contrôle de la copie) :Donc, au lieu de "pourquoi C ++ n'a-t-il pas de valeur par défaut
operator==()
?", La question aurait dû être "pourquoi C ++ a-t-il un constructeur d'affectation et de copie par défaut?", La réponse étant que ces éléments ont été inclus à contrecœur par Stroustrup pour une compatibilité descendante avec C (probablement la cause de la plupart des verrues de C ++, mais aussi probablement la principale raison de la popularité de C ++).À mes propres fins, dans mon IDE, l'extrait de code que j'utilise pour les nouvelles classes contient des déclarations pour un opérateur d'affectation privé et un constructeur de copie, de sorte que lorsque je crée une nouvelle classe, je n'obtiens aucune affectation par défaut et aucune opération de copie - je dois supprimer explicitement la déclaration de ces opérations dans la
private:
section si je veux que le compilateur puisse les générer pour moi.la source
Foo(const Foo&) = delete; // no copy constructor
etFoo& Foo=(const Foo&) = delete; // no assignment operator
struct
, mais je souhaite qu'il laisseclass
se comporter différemment (et sainement). Dans le processus, cela aurait également donné une différence plus significative entrestruct
et àclass
côté de l'accès par défaut.operator==
. À ce stade, il ne s'agit que de sucre de syntaxe pour un code de plaque de chaudière. Si vous craignez que de cette façon le programmeur puisse ignorer un pointeur parmi les champs de classe, vous pouvez ajouter une condition selon laquelle il ne peut fonctionner que sur les types primitifs et les objets qui ont eux-mêmes des opérateurs d'égalité. Cependant, il n'y a aucune raison de rejeter entièrement cela.Même en C ++ 20, le compilateur ne génère toujours pas implicitement
operator==
pour vousMais vous aurez la possibilité de explicitement par défaut
==
depuis C ++ 20 :Défaillant
==
fait sage membre==
(de la même manière que le constructeur de copie par défaut ne construction de copie-sage membre). Les nouvelles règles fournissent également la relation attendue entre==
et!=
. Par exemple, avec la déclaration ci-dessus, je peux écrire les deux:Cette fonctionnalité spécifique (défaut
operator==
et symétrie entre==
et!=
) provient d' une proposition qui faisait partie de la fonctionnalité de langage plus large qui estoperator<=>
.la source
= default
, pour une chose qui n'est pas créée par défaut, non? Cela ressemble à de l'oxymore pour moi ("défaut explicite").À mon humble avis, il n'y a pas de "bonne" raison. La raison pour laquelle tant de gens sont d'accord avec cette décision de conception est qu'ils n'ont pas appris à maîtriser la puissance de la sémantique basée sur les valeurs. Les gens ont besoin d'écrire beaucoup de constructeur de copie personnalisé, d'opérateurs de comparaison et de destructeurs car ils utilisent des pointeurs bruts dans leur implémentation.
Lorsque vous utilisez des pointeurs intelligents appropriés (comme std :: shared_ptr), le constructeur de copie par défaut est généralement correct et l'implémentation évidente de l'opérateur de comparaison par défaut hypothétique est aussi fine.
la source
Il est répondu que C ++ n'a pas fait == parce que C n'a pas fait, et voici pourquoi C fournit uniquement default = mais pas == à la première place. C voulait rester simple: C implémenté = par memcpy; cependant, == ne peut pas être implémenté par memcmp en raison du remplissage. Parce que le remplissage n'est pas initialisé, memcmp dit qu'ils sont différents même s'ils sont identiques. Le même problème existe pour les classes vides: memcmp dit qu'elles sont différentes car la taille des classes vides n'est pas nulle. On peut voir d'en haut que l'implémentation de == est plus compliquée que l'implémentation de = en C. Quelques exemples de code à ce sujet. Votre correction est appréciée si je me trompe.
la source
operator=
- cela ne fonctionnerait que pour les types POD, mais C ++ fournit également une valeuroperator=
par défaut pour les types non POD.Dans cette vidéo Alex Stepanov, le créateur de STL répond à cette même question vers 13h00. Pour résumer, après avoir observé l'évolution du C ++, il soutient que:
Il dit ensuite que dans un avenir (lointain) == et ! = Seront implicitement générés.
la source
C ++ 20 permet d'implémenter facilement un opérateur de comparaison par défaut.
Exemple de cppreference.com :
la source
Point
comme exemple pour une opération de commande , car il n'y a pas de moyen par défaut raisonnable pour commander deux points avecx
ety
coordonnées ...std::set
pour vous assurer que tous les points sont uniques etstd::set
utilisentoperator<
uniquement.auto
: Dans ce cas, pouvons-nous toujours supposer qu'il proviendrastd::strong_ordering
de#include <compare>
?std::common_comparison_category_t
, qui pour cette classe devient le classement par défaut (std::strong_ordering
).Il n'est pas possible de définir la valeur par défaut
==
, mais vous pouvez définir la valeur!=
par défaut via==
laquelle vous devez généralement vous définir. Pour cela, vous devez faire les choses suivantes:Vous pouvez consulter http://www.cplusplus.com/reference/std/utility/rel_ops/ pour plus de détails.
De plus, si vous définissez
operator<
, les opérateurs pour <=,>,> = peuvent en être déduits lors de l'utilisationstd::rel_ops
.Mais vous devez être prudent lorsque vous utilisez
std::rel_ops
car les opérateurs de comparaison peuvent être déduits pour les types auxquels vous n'êtes pas attendu.La manière la plus préférée de déduire un opérateur lié d'un opérateur de base est d'utiliser boost :: operators .
L'approche utilisée dans boost est meilleure car elle définit l'utilisation de l'opérateur pour la classe que vous souhaitez uniquement, pas pour toutes les classes de la portée.
Vous pouvez également générer "+" à partir de "+ =", - à partir de "- =", etc ... (voir la liste complète ici )
la source
!=
après avoir écrit l'==
opérateur. Ou je l'ai fait, mais cela manquaitconst
. J'ai dû l'écrire moi aussi et tout allait bien.rel_ops
déconseillée en C ++ 20: parce que cela ne fonctionne pas , du moins pas partout, et certainement pas de manière cohérente. Il n'y a aucun moyen fiablesort_decreasing()
de compiler. D'un autre côté, Boost.Operators fonctionne et a toujours fonctionné.C ++ 0x
aeu une proposition de fonctions par défaut, vous pouvez donc dire quedefault operator==;
nous avons appris que cela aide à rendre ces choses explicites.la source
operator==
. C'est dommage.Conceptuellement, il n'est pas facile de définir l'égalité. Même pour les données POD, on pourrait faire valoir que même si les champs sont les mêmes, mais qu'il s'agit d'un objet différent (à une adresse différente), il n'est pas nécessairement égal. Cela dépend en fait de l'utilisation de l'opérateur. Malheureusement, votre compilateur n'est pas psychique et ne peut pas en déduire.
En plus de cela, les fonctions par défaut sont d'excellents moyens de se tirer une balle dans le pied. Les valeurs par défaut que vous décrivez sont essentiellement là pour maintenir la compatibilité avec les structures POD. Cependant, ils causent plus que suffisamment de ravages, les développeurs les oubliant ou la sémantique des implémentations par défaut.
la source
int
créé via la copie ctor d'un autre est égal à celui à partir duquel il a été créé; la seule chose logique à faire pour l'unstruct
des deuxint
domaines est de travailler exactement de la même manière.+
opérateur en ce qu'il n'est pas associatif pour les flottants; c'est-à-dire(x + y) + z
! =x + (y + z)
, en raison de la façon dont l'arrondi FP se produit. (Il s'agit sans doute d'un problème bien pire que==
parce qu'il est vrai pour les valeurs numériques normales.) Vous pouvez suggérer d'ajouter un nouvel opérateur d'addition qui fonctionne pour tous les types numériques (même int) et qui est presque exactement le même+
mais associatif ( en quelque sorte). Mais alors, vous ajouteriez des ballonnements et de la confusion à la langue sans vraiment aider autant de gens.Ce n'est peut-être pas un problème sur le plan fonctionnel, mais en termes de performances, la comparaison membre par membre par défaut est susceptible d'être plus sous-optimale que l'attribution / copie membre par membre par défaut. Contrairement à l'ordre d'affectation, l'ordre de comparaison a un impact sur les performances car le premier membre inégal implique que le reste peut être ignoré. Donc, s'il y a des membres qui sont généralement égaux, vous voulez les comparer en dernier, et le compilateur ne sait pas quels membres sont plus susceptibles d'être égaux.
Considérez cet exemple, où
verboseDescription
est une longue chaîne sélectionnée parmi un ensemble relativement petit de descriptions météorologiques possibles.(Bien sûr, le compilateur aurait le droit de ne pas tenir compte de l'ordre des comparaisons s'il reconnaît qu'il n'y a pas d'effets secondaires, mais il est probable qu'il tirera toujours son nom du code source où il ne dispose pas de meilleures informations.)
la source
Juste pour que les réponses à cette question restent complètes au fil du temps: depuis C ++ 20 il peut être généré automatiquement avec la commande
auto operator<=>(const foo&) const = default;
Il générera tous les opérateurs: ==,! =, <, <=,> Et> =, voir https://en.cppreference.com/w/cpp/language/default_comparisons pour plus de détails.
En raison de l'apparence de l'opérateur
<=>
, il est appelé opérateur de vaisseau spatial. Voir aussi Pourquoi avons-nous besoin de l'opérateur <=> du vaisseau spatial en C ++? .EDIT: également en C ++ 11 un substitut assez soigné qui est disponible avec
std::tie
voir https://en.cppreference.com/w/cpp/utility/tuple/tie pour un exemple de code complet avecbool operator<(…)
. La partie intéressante, modifiée pour fonctionner avec==
est:std::tie
fonctionne avec tous les opérateurs de comparaison et est complètement optimisé par le compilateur.la source
Je suis d'accord, pour les classes de type POD, le compilateur pourrait le faire pour vous. Cependant, ce que vous pourriez considérer comme simple, le compilateur peut se tromper. Il est donc préférable de laisser le programmeur le faire.
J'ai eu un cas POD une fois où deux des champs étaient uniques - donc une comparaison ne serait jamais considérée comme vraie. Cependant, la comparaison dont j'avais besoin n'a jamais été comparée que sur la charge utile - quelque chose que le compilateur ne comprendrait jamais ou ne pourrait jamais comprendre par lui-même.
D'ailleurs - ils ne tardent pas à écrire, n'est-ce pas?!
la source