2016-02-29 5 views
31

多くの属性を持つクラスに対してC++で==演算子をオーバーロードする必要があります。
すべての属性が等しい場合にのみ、演算子はtrueを返します。バグを避けるために、これらの属性が時間とともに変化するならば、ショートカットが便利かもしれません。C++の等価(==)オーバーロード、すべての属性を比較するショートカットまたは最良の方法

クラス内のすべての属性を比較するためのショートカットはありますか?

+0

あなたのオブジェクトがPODまたはその大部分のPOD(その部分)の場合はmemcmpを使うことができます – VladimirS

+0

スクリプトを書くことができます(エディタがサポートしていればそれを編集することができます)宣言の行をコピーして 'element == other.element && 'にしてください。 –

+8

@ user3545806' memcmp'は埋め込みを考慮しないため、動作しません。 – Barry

答えて

40

ショートカットはありません。すべてを一覧表示する必要があります。

エラーのいくつかのソースは以下のようにtied()という名前のメンバ関数を導入することで軽減することができます:あなたのoperator==はちょうどそれを使用できるようにすることを

struct Foo { 
    A a; 
    B b; 
    C c; 
    ... 

private: 
    auto tied() const { return std::tie(a, b, c, ...); } 
}; 

bool operator==(Foo const& rhs) const { return tied() == rhs.tied(); } 

これは、あなただけのすべてを一覧表示することができますメンバーは一度。しかし、それはそれについてです。あなたはまだそれらを実際にリストする必要があります(あなたはまだそれを忘れることができます)。


はデフォルトoperator==を作成するための提案(P0221R0)があり、それが受け入れてしまいます場合、私は知りません。

+0

@Downvoterどうしたの? – Barry

2

これを実行する唯一の方法は、不幸にもすべての属性をチェックすることです。良いことは、&&を使用してすべての小切手を組み合わせると、最初の偽の陳述の後に評価が中止されることです。 (短絡評価)

false && (4 == 4)。プログラムは4 == 4の部分を評価することはありません。&&で結合されたすべてのステートメントはtrueとなる必要があり、最終的な結果としてtrueになる必要があるためです。これは理にかなっていますか?

+0

これを短絡評価といいます。それには他の方法はありません。 – erip

+0

さらに、ここで連結された単語は正しい単語ではありません。 – erip

+0

@eripごめんなさい。私はネイティブスピーカーではありません。この状況では何が良い言葉でしょうか? – muXXmit2X

16

tuplesの導入によるC++ 11以降では、std::tie()も得ました。これにより、変数の束からタプルを作り、そのすべてに対して比較関数を呼び出すことができます。あなたはそれを好きなように使うことができます

struct Foo 
{ 
    int a,b,c,d,e,f; 
    bool operator==(const Foo& rhs) { return std::tie(a,b,c,d,e,f) == std::tie(rhs.a,rhs.b,rhs.c,rhs.d,rhs.e,rhs.f); } 
}; 

あなたはまだチェックしたいすべてのメンバーをリストアップする必要がありますが、それは簡単です。これを使用して、より小さい値と大きい値の比較をより簡単に行うこともできます。

変数は、tieに指定した順序でチェックされます。これは、比較よりも小さくても大きくても重要です。

std::tie(a,b) < std::tie(rhs.a, rhs.b); 

は、現時点では

std::tie(b,a) < std::tie(rhs.b, rhs.a); 
5

と同じである必要はありません、近道はありませんが、それ(P0221R0)のサポートを追加する計画があります。

ビャーネ・ストロヴストルップは最近、それについてのブログ記事を書いた:C++ 14では A bit of background for the default comparison proposal

を、エラーが発生しやすくなり、すべてのメンバーをリストアップし、それらを比較することに勝るものは、ありません。Bjarneを引用するには:

デフォルト比較のためのkiller引数は、実際には便宜的ではありませんが、人々が等価演算子を誤って取得するという事実です。

1

関連する解決策はありません。いわゆるX-Macroの助けを借りて定義テーブルから関連コードを生成することができます。表は

#define MEMBER_TBL     \ 
/*type  ,name ,default*/  \ 
X(int   ,_(i) ,42 )   \ 
X(float  ,_(f) ,3.14 )   \ 
X(std::string , t ,"Hello")   \ 

ようになり_()ものはstd::tie()コール世代の末尾,を避けるために必要とされます。最後の要素がw.oであることを確認します。 _()。メンバーを生成するための使用方法は次のとおりです。

struct Foo 
{ 
#define _(x) x 
#define X(type, name, default) type name{default}; 
    MEMBER_TBL 
#undef X 
#undef _ 
} 

これが生成します。

struct Foo 
{ 
    int i{42}; float f{3.14}; std::string t{"Hello"}; 
} 

operator==あなたが使用することができますを生成するには:

bool operator==(Foo const& other) const { 
    return std::tie(
        this->i, this->f, this->t 
    ) == std::tie(
        other.i, other.f, other.t 
    ); 
} 

になり

bool operator==(Foo const& other) const { 
     return std::tie(
#define _(x) x, 
#define X(type, name, default) this->name 
      MEMBER_TBL 
#undef X 
     ) == std::tie(
#define X(type, name, default) other.name 
      MEMBER_TBL 
#undef X 
#undef _ 
     ); 
    } 

nを追加するには最初のテーブルに単純に新しいエントリを追加することができます。それ以外は自動的に生成されます。

もう一つの利点は、あなたは、単にメンバーにに関するすべての情報が一箇所(単一のテーブルに追加することができ

void print() const { 
    std::cout << "i" << ": " << i << " "; std::cout << "f" << ": " << f << " "; std::cout << "t" << ": " << t << " "; 
    std::cout << std::endl; 
} 

になり

void print(void) const { 
    #define STR(x) #x 
    #define _(x) x 
    #define X(type, name, default)   \ 
      std::cout <<      \ 
       STR(name) << ": " << name << " "; 
      MEMBER_TBL 
    #undef X 
    #undef _ 
    #undef STR 
      std::cout << std::endl; 
     } 

ようdump()メソッドを追加することができ、あります情報のポイント)、必要な場所で抽出されます。

A作業Demo

+0

このテクニックは非常に外出する場合にのみ予約する必要があります。たとえば、設定パラメータのクラスがある例で使用しました。新しいパラメータを導入する必要があるときはいつでも、コード内のいくつかの場所を更新する必要がありましたが、これはかなりエラーが発生しやすいものでした。 IMHOはXマクロアプローチを使用してメンテナンスを簡素化しましたが、それは少し議論の余地がありますので、最後の手段として推奨します。しかし、その技術が存在することを知っていることは、本当に有用です。 –

関連する問題