51 votes

Pourrais-je utiliser l'opérateur == si j'ai seulement implémenté l'opérateur <?

J'ai implémenté operator< pour un certain objet. Logiquement, si !(a < b) et !(b < a) cela signifie que a == b.

Est-ce que cela est déduit automatiquement ? Puis-je utiliser == si j'ai seulement implémenté < ?

6voto

nefas Points 926

Le compilateur n'infère pas == à partir de <.

Vous pouvez vérifier cela avec un exemple simple:

#include 

struct A {
    A(int r):i{r}{}
    int i;
};

bool operator<(A const & a1, A const& a2) {
    return a1.i < a2.i;
}

int main(int argc, char* argv[]) {
    A a1{2};
    A a2{3};
    if(a1 == a2) {
        std::cout << "égal\n";
    }
    return 0;
}

GCC vous donne cette erreur:

main.cpp:20:11: erreur: pas de correspondance pour l'opérateur '==' (types d'opérandes sont 'A' et 'A')

     if(a1 == a2) {

6voto

Marek R Points 4503

Il existe des modèles définis dans l'espace de noms std::rel_ops qui définissent automatiquement les opérateurs manquants.

Cela ne définit pas un opérateur d'égalité basé sur l'opérateur de moins que vous souhaitez.

Cependant, cela reste très utile; si vous définissez l'opérateur de moins que et l'opérateur d'égalité, vous obtiendrez gratuitement les autres opérateurs de comparaison.

6voto

Yakk Points 31636

Comme beaucoup l'ont affirmé, non vous ne pouvez pas, et non le compilateur ne devrait pas le faire.

Cela ne signifie pas que cela ne devrait pas être facile de passer d'un < à un == et tout le reste.

boost::operators tente de le rendre facile. Utilisez-le et c'est fait.

Si vous voulez le faire vous-même, il ne suffit que d'un peu de code pour réimplémenter ce que boost vous fournit :

namespace utility {
  namespace details {
    templateusing void_t=void;
    templateclass Z, class, class...Ts>
    struct can_apply:std::false_type{};
    templateclass Z, class...Ts>
    struct can_apply>, Ts...>:std::true_type{};
  }
  templateclass Z, class...Ts>
  using can_apply = ::utility::details::can_apply;
}

namespace auto_operators {
  template
  using less_r = decltype( std::declval() < std::declval() );
  template
  using can_less = ::utility::can_apply;

  struct order_from_less {
    template
    using enabled = std::enable_if_t<
      std::is_base_of{}
      && std::is_base_of{}
      && can_less{},
      bool
    >;
    template
    friend enabled
    operator>(T const& lhs, U const& rhs) {
      return rhs < lhs;
    }
    template
    friend enabled
    operator<=(T const& lhs, U const& rhs) {
      return !(lhs > rhs);
    }
    template
    friend enabled
    operator>=(T const& lhs, U const& rhs) {
      return !(lhs < rhs);
    }
  };
  struct equal_from_less:order_from_less {
    template
    using enabled = std::enable_if_t<
      std::is_base_of{}
      && std::is_base_of{}
      && can_less{} && can_less{},
      bool
    >;
    template
    friend enabled
    operator==(T const& lhs, U const& rhs) {
      return !(lhs < rhs) && !(rhs < lhs);
    }
    template
    friend enabled
    operator!=(T const& lhs, U const& rhs) {
      return !(lhs==rhs);
    }
  };
}

Le code ci-dessus n'a besoin d'être écrit qu'une fois, ou le code équivalent obtenu depuis #include boost.

Une fois que vous avez boost, ou le code ci-dessus, c'est aussi simple que quelque chose comme :

struct foo : auto_operators::equal_from_less {
  int x;
  foo( int in ):x(in) {}
  friend bool operator<( foo const& lhs, foo const& rhs ) {
    return lhs.x < rhs.x;
  }
};

et foo a maintenant tous les opérateurs de comparaison définis sur lui.

int main() {
  foo one{1}, two{2};
  std::cout << (one < two) << "\n";
  std::cout << (one > two) << "\n";
  std::cout << (one == two) << "\n";
  std::cout << (one != two) << "\n";
  std::cout << (one <= two) << "\n";
  std::cout << (one >= two) << "\n";
  std::cout << (one == one) << "\n";
  std::cout << (one != one) << "\n";
  std::cout << (one <= one) << "\n";
  std::cout << (one >= one) << "\n";
}

Exemple en direct.

Le but de tout cela est que C++ ne suppose pas, en tant que langage, que < signifie > et que >= et == ont tous un sens. Mais vous pouvez écrire une bibliothèque qui vous permet de prendre un type avec < défini, et en ajoutant une classe de base triviale, tous ces autres opérations définies avec aucun coût d'exécution.

5voto

xiaobing Points 193

La réponse est NON, vous avez juste besoin d'un test simple

struct MyType{
    int value;
};

bool operator < (MyType& a, MyType& b)
{
    return a.value < b.value;
}

int main(int argc, char* argv[])
{
    MyType a = {3};
    MyType b = {4};
    if (a == b)
        std::cout << "a==b" << std::endl;
    if (a < b)
        std::cout << "a < b" << std::endl;
}

g++ 4.8.2 se plaint :

main.cpp: Dans la fonction ‘int main(int, char**)’ :

main.cpp:16:11: erreur: aucun correspondance pour ‘operator==’ (les types des opérandes sont ‘MyType’ et ‘MyType’)

Mais il y a quelque chose de similaire qui fonctionne en C++, vérifiez ce c++ concepts:Compare

il dit:

equiv(a, b), une expression équivalente à !comp(a, b) && !comp(b, a)

4voto

Shadi Points 1256

En plus d'autres réponses,

Le compilateur ne peut même pas déduire != à partir de ==

struct MyType
{
    int value;
};

bool operator == (const MyType& a, const MyType& b)
{
    return a.value == b.value;
}

int main()
{
    MyType a = {3};
    MyType b = {4};
    if (a != b)    // (* Erreur de compilation *) 
        std::cout << "a n'est pas égal à b" << std::endl;
}

Cependant, ce serait bien s'il existait une option pour indiquer au compilateur que le reste des opérateurs rationnels s'appliquent à votre classe.

Il y a, comme expliqué dans certaines réponses dans l'en-tête quelque chose qui peut fournir une telle fonctionnalité. Vous devrez ajouter la ligne suivante au début du main:

using namespace std::rel_ops; 

Cependant, l'utilisation de cette approche est coûteuse et entraînera des ambiguïtés de surcharge un peu partout comme l'a noté JDługosz.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X