web-dev-qa-db-ja.com

C ++以上の演算子

C++では、より大きい(または等しい)演算子( "> =")に対して、等しい( "=")およびより大きい( ">")演算子をオーバーロードして、以上( "> =")?または、機能を持たせるために演算子( "> =")をオーバーロードする必要がありますか?

14
Vasi Marin

明らかな表記法「> || == "は、実際には>=

all関係演算子の場合、実際に必要なのは<a < bおよびb < aは両方とも偽です。実際、これは、順序付けられたC++標準ライブラリコンテナで使用される概念の1つです。

9
Bathsheba

operator >=は、operator >operator =の組み合わせではありません。 operator >=は独自の演算子ですが、operator <の観点から実装できます。通常、次のようなものがあります。

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);}

Sbiから answer on 演算子のオーバーロードの基本的なルールとイディオムは何ですか?

14
NathanOliver

演算子が等しい( "=")で十分ですか

C++の等しい演算子は==です

または、演算子( "> =")に機能を持たせるために演算子をオーバーロードする必要がありますか?

機能の意味によって異なります。 operator==operator>を定義すると、コンパイラーはoperator>=を自動的に生成しますか?いいえ、ありません。既存の演算子を使用して実装する必要があります。

13
Slava

いいえ、C++はこれらの演算子を作成しません。

それが悪いと思うなら、あなたは正しい。この吸い込みを減らすための多くの方法が行われました。それらのうち4つについて説明します。

c ++ 2 を待つ

c ++ 2 で、 operator<=>-way "spaceship"演算子)を正しく記述した場合、または=defaultそれから、<<=>=>!=、および==のすべてが自動的に書き込まれます。

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

上記のbobには、C++によって書かれたすべての<== etc演算子があります。

書くだけ

c ++ 2 の前に、それらのすべてが必要な場合は、それらすべてを記述する必要があります。これは退屈でエラーが発生しやすくなります。

std::tieを使用して<などを呼び出すと、エラーが発生しにくくなります。

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);
  }
};

あるいは

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);
  }
};

Tupleは適切な辞書比較を行うためです。バグなく辞書式の比較を書くのは面倒です。

あなたのやり方でメタプログラミングする

文字列を比較するときは、通常strcmpを使用します。これは、小さい場合は負の数、大きい場合は正の数、等しい場合は0を返します。このパターンは、<または==を繰り返し実行するよりも効率的です。

単一のstrcmpのような関数を作成すると<==が生成され、他の比較操作を実行できます。

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); }
  };
}

ここで、型があるとします。

struct bob {
  int x, y;
};

そして、それに比較演算子を使用できるようにしたい:

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;
  }
};

[〜#〜] crtp [〜#〜]bobの魔法を使用すると、すべての比較演算子が書き込まれます。

実例

面倒なfriend int cmp(これに含まれるメンバーが増えると面倒になります)は、さらに定型的なヘルパーコードで処理できます。

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;
  }
}

これはさらに難解なコードですが、より単純なbobを取得します。

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) );
  }
};

ただし、これはすべて、 c ++ 2operator<=>によって実行され、改善されることに注意してください。

実例

他の人のソリューションを使用する

これは、 boost :: operators がこれらの演算子を作成するために使用するアプローチに似ています。