Langage de programmation C++ : l’opérateur d’égalité généré automatiquement

Langage de programmation C++ : l’opérateur d’égalité généré automatiquement

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 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.

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.

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)

Vers la page d'accueil



#Langage #programmation #lopérateur #dégalité #généré #automatiquement
1704839811

Facebook
Twitter
LinkedIn
Pinterest

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.