54
votes

l'opérateur non défini par défaut <=> ne génère pas == et! = en C ++ 20

Je rencontre un comportement étrange avec le nouvel opérateur de vaisseau spatial <=> en C ++ 20. J'utilise le compilateur Visual Studio 2019 avec /std:c++latest .

Ce code se compile correctement, comme prévu:

struct X
{
    int Dummy = 0;
    auto operator<=>(const X& other) const
    {
        return Dummy <=> other.Dummy;
    }
};

Cependant, si je change X en ceci:

#include <compare>

struct X
{
    int Dummy = 0;
    auto operator<=>(const X&) const = default; // Default implementation
};

int main()
{
    X a, b;

    a == b; // OK!

    return 0;
}

J'obtiens l'erreur de compilateur suivante:

error C2676: binary '==': 'X' does not define this operator or a conversion to a type acceptable to the predefined operator

J'ai essayé cela sur clang aussi, et j'ai un comportement similaire.

J'apprécierais quelques explications sur la raison pour laquelle l'implémentation par défaut génère correctement l' operator== , mais pas celle personnalisée.


1 commentaires

Le titre rend plus difficile d'atteindre cette question lors de la recherche sur Google. Peut-être devrait-il passer à l' non-defaulted operator <=> doesn't generate == and != . Il m'est arrivé de rencontrer la motivation derrière p1185r2 et j'allais poser une question similaire et y répondre moi-même.


3 Réponses :


48
votes

Lors de la standardisation de cette fonctionnalité, il a été décidé que l'égalité et l'ordre devraient être logiquement séparés. Ainsi, les utilisations des tests d'égalité ( == et != ) N'appelleront jamais l' operator<=> . Cependant, il était toujours considéré comme utile de pouvoir utiliser les deux par défaut avec une seule déclaration. Donc, si vous avez l' operator<=> par défaut operator<=> , il a été décidé que vous vouliez également l' operator== par défaut operator== (à moins que vous ne le définissiez plus tard ou que vous l'ayez défini plus tôt).

Quant à savoir pourquoi cette décision a été prise , le raisonnement de base est le suivant. Considérez std::string . L'ordre de deux chaînes est lexicographique; chaque caractère a sa valeur entière comparée à chaque caractère de l'autre chaîne. La première inégalité résulte du résultat de l'ordre.

Cependant, le test d'égalité des chaînes présente un court-circuit. Si les deux chaînes ne sont pas de longueur égale, alors il est inutile de faire une comparaison par caractère; ils ne sont pas égaux. Donc, si quelqu'un fait des tests d'égalité, vous ne voulez pas le faire de manière longue si vous pouvez le court-circuiter.

Il s'avère que de nombreux types qui nécessitent un ordre défini par l'utilisateur offriront également un mécanisme de court-circuit pour les tests d'égalité. Pour empêcher les gens d'implémenter uniquement l' operator<=> et de gâcher les performances potentielles, nous obligons effectivement tout le monde à faire les deux.


0 commentaires

52
votes

C'est par conception.

[class.compare.default] (c'est moi qui souligne)

3 Si la définition de classe ne déclare pas explicitement une fonction d'opérateur == , mais déclare une fonction d' opérateur de comparaison à trois voies par défaut , une fonction d'opérateur == est déclarée implicitement avec le même accès que la fonction d'opérateur de comparaison à trois. L'opérateur == déclaré implicitement pour une classe X est un membre en ligne et est défini par défaut dans la définition de X.

Seul un <=> défaut permet à un == synthétisé d'exister. Le raisonnement est que les classes comme std::vector ne peuvent pas utiliser un <=> défaut. De plus, utiliser <=> pour == n'est pas le moyen le plus efficace de comparer des vecteurs. <=> doit donner l'ordre exact, tandis que == peut renflouer plus tôt en comparant d'abord les tailles.

Si une classe fait quelque chose de spécial dans sa comparaison à trois, elle devra probablement faire quelque chose de spécial dans son == . Ainsi, au lieu de générer un défaut non sensé, le langage laisse le soin au programmeur.


2 commentaires

C'est certainement judicieux, à moins que le vaisseau spatial ne soit buggé. Mais potentiellement extrêmement inefficace ...


@Deduplicator - La sensibilité est subjective. Certains diraient qu'une mise en œuvre inefficace générée silencieusement n'est pas raisonnable.



17
votes

Les autres réponses expliquent très bien pourquoi la langue est comme ça. Je voulais juste ajouter qu'au cas où ce ne serait pas évident, il est bien sûr possible d'avoir un operator<=> fourni par l'utilisateur operator<=> avec un operator== défaut operator== . Il vous suffit d'écrire explicitement l' operator== défaut operator== :

struct X
{
    int Dummy = 0;
    auto operator<=>(const X& other) const
    {
        return Dummy <=> other.Dummy;
    }
    bool operator==(const X& other) const = default;
};


0 commentaires