web-dev-qa-db-fra.com

C ++ supérieur ou égal à l'opérateur

En C++, pour l'opérateur supérieur ou égal à ("> ="), suffit-il d'avoir les opérateurs égaux ("=") et supérieurs (">") surchargés pour disposer de fonctionnalités supérieures ou égales à ( "> =")? Ou dois-je surcharger l'opérateur ("> =") pour avoir une fonctionnalité?

14
Vasi Marin

En utilisant une notation évidente, "> || == "est en fait une exigence excessive pour >=.

Notez bien que pour tous les opérateurs relationnels, vous n’avez réellement besoin que de <, puisque l’équivalence est établie si a < b et b < a sont tous deux faux. En fait, il s'agit d'un des concepts utilisés dans les conteneurs de bibliothèques standard C++ ordonnés.

9
Bathsheba

operator >= n'est pas une combinaison de operator > et operator =. operator >= est son propre opérateur mais vous pouvez l'implémenter en termes de operator < Typiquement, vous auriez quelque chose comme

inline bool operator==(const X& lhs, const X& rhs){ /* do actual comparison */ }
inline bool operator!=(const X& lhs, const X& rhs){return !operator==(lhs,rhs);}
inline bool operator< (const X& lhs, const X& rhs){ /* do actual comparison */ }
inline bool operator> (const X& lhs, const X& rhs){return  operator< (rhs,lhs);}
inline bool operator<=(const X& lhs, const X& rhs){return !operator> (lhs,rhs);}
inline bool operator>=(const X& lhs, const X& rhs){return !operator< (lhs,rhs);}

De sbi's answer on Quelles sont les règles de base et les idiomes pour la surcharge des opérateurs?

14
NathanOliver

est-il suffisant d'avoir les opérateurs égaux ("=")

L'opérateur égal en c ++ est ==

OU dois-je surcharger l'opérateur ("> =") pour avoir une fonctionnalité?

Cela dépend de ce que vous entendez par fonctionnalité. Si vous voulez dire que si vous définissez operator== et operator> le compilateur générera operator>= automatiquement pour vous? Non, ce ne serait pas le cas, vous devez le mettre en œuvre en utilisant des opérateurs existants ou non.

13
Slava

Non, C++ n'écrit pas ces opérateurs pour vous.

Si vous pensez que ça craint, vous avez raison. Une foule de façons de faire en sorte que ça craigne moins ont été faites. Je vais parler de 4 d'entre eux.

Attendez c ++ 2

Dans c ++ 2 , si vous écrivez operator<=> (l'opérateur à 3 voies "vaisseau spatial") correctement, ou =default ça, alors tout <, <=, >=, >, != et == sera écrit pour vous.

struct bob {
  int x,y;
  auto operator<=>( bob const& )const = default;
};

Le bob ci-dessus a tous les <== etc opérateur écrit pour cela par C++ maintenant.

Il suffit d'écrire

Avant c ++ 2 vous devez tous les écrire si vous voulez tous. C'est fastidieux et sujet aux erreurs.

En utilisant std::tie et en invoquant < et ainsi de suite est légèrement moins sujet aux erreurs:

struct bob {
  int x, y;
  friend bool operator<( bob const& lhs, bob const& rhs ) {
    return std::tie(lhs.x, lhs.y) < std::tie(rhs.x, rhs.y);
  }
};

ou même

struct bob {
  int x, y;
  friend auto as_tie( bob const& b ) { // C++14
    return std::tie(b.x, b.y);
  }
  friend bool operator<( bob const& lhs, bob const& rhs ) {
    return as_tie(lhs) < as_tie(rhs);
  }
};

parce que Tuple effectue une comparaison lexographique appropriée; écrire des comparaisons lexographiques sans bugs est ennuyant.

Métaprogrammes à votre guise

Lorsque vous comparez des chaînes, vous utilisez généralement strcmp. Cela renvoie un nombre négatif si inférieur, un nombre positif si supérieur et 0 si égal. Ce modèle peut être plus efficace que de faire < ou == à plusieurs reprises.

Créer une fonction similaire strcmp comme produit <== et les autres opérations de comparaison peuvent être effectuées:

namespace utils {
  template<class D>
  struct use_cmp {
    friend bool operator<( use_cmp<D> const& lhs, use_cmp<D> const& rhs ) {
      return cmp( lhs.self(), rhs.self() ) < 0;
    }
    friend bool operator>( use_cmp<D> const& lhs, use_cmp<D> const& rhs ) {
      return cmp( lhs.self(), rhs.self() ) > 0;
    }
    friend bool operator<=( use_cmp<D> const& lhs, use_cmp<D> const& rhs ) {
      return cmp( lhs.self(), rhs.self() ) <= 0;
    }
    friend bool operator>=( use_cmp<D> const& lhs, use_cmp<D> const& rhs ) {
      return cmp( lhs.self(), rhs.self() ) >= 0;
    }
    friend bool operator==( use_cmp<D> const& lhs, use_cmp<D> const& rhs ) {
      return cmp( lhs.self(), rhs.self() ) == 0;
    }
    friend bool operator!=( use_cmp<D> const& lhs, use_cmp<D> const& rhs ) {
      return cmp( lhs.self(), rhs.self() ) != 0;
    }
  private:
    D const& self() const { return *static_cast<D const*>(this); }
  };
}

Supposons maintenant que nous avons un type:

struct bob {
  int x, y;
};

et nous voulons pouvoir utiliser des opérateurs de comparaison:

struct bob : utils::use_cmp<bob>
{
  int x, y;
  bob( int x_, int y_ ):x(x_), y(y_) {} // constructor
  friend int cmp( bob const& lhs, bob const& rhs ) {
    if (lhs.x < rhs.x) return -1;
    if (lhs.x > rhs.x) return 1;
    if (lhs.y < rhs.y) return -1;
    if (lhs.y > rhs.y) return 1;
    return 0;
  }
};

et en utilisant la magie de CRTP bob a maintenant tous les opérateurs de comparaison écrits pour celui-ci.

Exemple en direct .

Cet ennuyeux friend int cmp (qui devient d'autant plus ennuyeux que vous avez plus de membres) peut être manipulé avec encore plus de code d'assistance standard:

namespace utils {
  template<class...Ts>
  int cmp( std::Tuple<Ts...> const& lhs, std::Tuple<Ts...> const& rhs );
  template<class T, class...LowPriority>
  int cmp( T const& lhs, T const& rhs, LowPriority&&... );

  template<class...Ts, std::size_t...Is>
  int Tuple_cmp( std::Tuple<Ts...> const& lhs, std::Tuple<Ts...> const& rhs, std::index_sequence<Is...> ) {
    int result = 0;
    ( (result = cmp( std::get<Is>(lhs), std::get<Is>(rhs) )) && ... );
    return result;
  }

  template<class...Ts>
  int cmp( std::Tuple<Ts...> const& lhs, std::Tuple<Ts...> const& rhs ) {
     return Tuple_cmp( lhs, rhs, std::make_index_sequence<sizeof...(Ts)>{} );
  }
  template<class T, class...LowPriority>
  int cmp( T const& lhs, T const& rhs, LowPriority&&... ) {
    if (lhs < rhs) return -1;
    if (rhs < lhs) return 1;
    return 0;
  }
}

qui est encore du code plus arcanique, mais vous obtenez un bob plus simple:

struct bob : utils::use_cmp<bob>
{
  int x, y;
  bob( int x_, int y_ ):x(x_), y(y_) {}

  friend auto as_tie(bob const& b) {
    return std::tie(b.x,b.y);
  }
  friend int cmp( bob const& lhs, bob const& rhs ) {
    return utils::cmp( as_tie(lhs), as_tie(rhs) );
  }
};

Notez cependant que tout cela est fait et amélioré par operator<=> dans c ++ 2 .

Exemple en direct .

Utilisez la solution de quelqu'un d'autre

Ceci est similaire à l'approche boost :: operators utilise pour écrire ces opérateurs pour vous.

11