115

次の構造体の2つのインスタンスを比較すると、エラーが発生します。

struct MyStruct1 {
    MyStruct1(const MyStruct2 &_my_struct_2, const int _an_int = -1) :
        my_struct_2(_my_struct_2),
        an_int(_an_int)
    {}

    std::string toString() const;

    MyStruct2 my_struct_2;
    int an_int;
};

エラーは次のとおりです。

エラーC2678:バイナリ'==':タイプ'myproj :: MyStruct1'の左側のオペランドをとる演算子が見つかりません(または受け入れ可能な変換がありません)

なんで?

4

8 に答える 8

150

C++ では、structs にはデフォルトで生成される比較演算子がありません。自分で書く必要があります:

bool operator==(const MyStruct1& lhs, const MyStruct1& rhs)
{
    return /* your comparison code goes here */
}
于 2011-04-21T06:25:17.530 に答える
114

C ++ 20では、デフォルトの比較、別名「スペースシップ」がoperator<=>導入されました。これにより、コンパイラで生成された</ <=/ ==/ !=/ >=/および/または>明白な/ナイーブ(?)実装の演算子を要求できます。

auto operator<=>(const MyClass&) const = default;

...ただし、より複雑な状況に合わせてカスタマイズできます(以下で説明します)。正当化と議論を含む言語提案については、ここを参照してください。operator<=>この回答は、C ++ 17以前、および...の実装をいつカスタマイズする必要があるかについての洞察に引き続き関連しています。

これを以前に標準化していないことはC++にとって少し役に立たないように思われるかもしれませんが、多くの場合、構造体/クラスには、比較から除外するデータメンバー(カウンター、キャッシュされた結果、コンテナー容量、最後の操作の成功/エラーコード、カーソルなど)があります。また、以下を含むがこれらに限定されない無数の事柄について行う決定。

  • どのフィールドを最初に比較するか。たとえば、特定のintメンバーを比較すると、99%の不均等なオブジェクトがすぐに削除される可能性がありますが、map<string,string>メンバーは同じエントリを持っていることが多く、比較するのに比較的費用がかかる可能性があります。値が実行時に読み込まれる場合、プログラマーは洞察を得ることができます。コンパイラはおそらくできません
  • 文字列の比較:大文字と小文字の区別、空白と区切り文字の同等性、エスケープ規則..
  • フロート/ダブルを比較するときの精度
  • NaN浮動小数点値が等しいと見なされるべきかどうか
  • ポインターまたはデータへのポインターの比較(後者の場合、ポインターが配列を指しているかどうか、および比較が必要なオブジェクト/バイトの数を知る方法)
  • 並べ替えられていないコンテナ(たとえばvectorlist)を比較するときに順序が重要かどうか、重要な場合は、比較する前にそれらをインプレースで並べ替えてもよいか、比較が行われるたびに追加のメモリを使用して一時的なコンテナを並べ替えてもよいかどうか
  • 比較する必要のある有効な値を現在保持している配列要素の数(サイズはどこかにありますか、それとも番兵ですか?)
  • union比較するaのメンバー
  • 正規化:たとえば、日付タイプでは範囲外の日または月が許可される場合があります。または、有理/分数オブジェクトの6/8が、別のオブジェクトの3/4がある場合があります。これは、パフォーマンス上の理由から修正されます。別の正規化ステップで怠惰に; 比較する前に、正規化をトリガーするかどうかを決定する必要がある場合があります
  • 弱ポインタ​​が有効でない場合の対処方法
  • 自分自身を実装していないメンバーとベースを処理する方法operator==(ただし、compare()またはoperator<またはstr()またはゲッターを持っている可能性があります...)
  • 他のスレッドが更新する可能性のあるデータの読み取り/比較中に取得する必要のあるロック

したがって、コンパイルさせても実行時に意味のある結果が得られないようにするのではなく、特定の構造に対して比較が何を意味するのかを明示的に考えるまで、エラーが発生するのは良いことです。

bool operator==() const = default;そうは言っても、「ナイーブな」メンバーごとの==テスト大丈夫だと判断したときに、C++で言わせていただければ幸いです。についても同じです!=。複数のメンバー/ベースを考えると、「デフォルト」、、、<および実装は絶望的ですが、宣言の順序に基づいてカスケードすることは可能ですが、メンバーの順序付けの命令が矛盾していることを考えると、望ましいものになる可能性はほとんどありません(ベースは必ずメンバーの前にあり、アクセシビリティ、依存使用前の構築/破壊)。より広く役立つためには、C ++は、選択をガイドするための新しいデータメンバー/ベースアノテーションシステムを必要とします-それは、理想的にはASTベースのユーザー定義コード生成と組み合わせて、標準で持つのは素晴らしいことです...私は期待していますそれ'<=>>=

等式演算子の典型的な実装

もっともらしい実装

合理的で効率的な実装は次のようになる可能性があります。

inline bool operator==(const MyStruct1& lhs, const MyStruct1& rhs)
{
    return lhs.my_struct2 == rhs.my_struct2 &&
           lhs.an_int     == rhs.an_int;
}

operator==これにもも必要であることに注意してくださいMyStruct2

この実装の意味と代替案については、以下のMyStruct1の詳細に関するディスカッションの見出しで説明します。

==、<、><=などへの一貫したアプローチ

std::tupleの比較演算子を利用して、独自のクラスインスタンスを比較するのは簡単です。これを使用std::tieして、フィールドへの参照のタプルを目的の比較順序で作成するだけです。ここから私の例を一般化する:

inline bool operator==(const MyStruct1& lhs, const MyStruct1& rhs)
{
    return std::tie(lhs.my_struct2, lhs.an_int) ==
           std::tie(rhs.my_struct2, rhs.an_int);
}

inline bool operator<(const MyStruct1& lhs, const MyStruct1& rhs)
{
    return std::tie(lhs.my_struct2, lhs.an_int) <
           std::tie(rhs.my_struct2, rhs.an_int);
}

// ...etc...

比較したいクラスを「所有」する場合(つまり、編集可能、企業およびサードパーティのライブラリの要素)、特にreturnステートメントから関数の戻り型を推測するC ++ 14の準備ができている場合は、「」を追加する方がよい場合がよくあります。比較できるようにしたいクラスに"メンバー関数を結び付けます。

auto tie() const { return std::tie(my_struct1, an_int); }

次に、上記の比較は次のように簡略化されます。

inline bool operator==(const MyStruct1& lhs, const MyStruct1& rhs)
{
    return lhs.tie() == rhs.tie();
}

より完全な比較演算子のセットが必要な場合は、ブースト演算子をお勧めします(を検索してless_than_comparableください)。何らかの理由で不適切な場合は、サポートマクロ(オンライン)のアイデアが好きかどうかはわかりません。

#define TIED_OP(STRUCT, OP, GET_FIELDS) \
    inline bool operator OP(const STRUCT& lhs, const STRUCT& rhs) \
    { \
        return std::tie(GET_FIELDS(lhs)) OP std::tie(GET_FIELDS(rhs)); \
    }

#define TIED_COMPARISONS(STRUCT, GET_FIELDS) \
    TIED_OP(STRUCT, ==, GET_FIELDS) \
    TIED_OP(STRUCT, !=, GET_FIELDS) \
    TIED_OP(STRUCT, <, GET_FIELDS) \
    TIED_OP(STRUCT, <=, GET_FIELDS) \
    TIED_OP(STRUCT, >=, GET_FIELDS) \
    TIED_OP(STRUCT, >, GET_FIELDS)

...それからそれを使用することができます...

#define MY_STRUCT_FIELDS(X) X.my_struct2, X.an_int
TIED_COMPARISONS(MyStruct1, MY_STRUCT_FIELDS)

(C ++ 14メンバータイバージョンはこちら

MyStruct1の詳細についてのディスカッション

自立型とメンバーを提供するという選択には影響がありoperator==()ます...

独立した実装

あなたには興味深い決断があります。クラスは暗黙的にから構築できるためMyStruct2、独立型/非メンバーbool operator==(const MyStruct2& lhs, const MyStruct2& rhs)関数は...をサポートします。

my_MyStruct2 == my_MyStruct1

...最初にMyStruct1から一時的なものを作成しmy_myStruct2、次に比較を行います。MyStruct1::an_intこれにより、コンストラクターのデフォルトのパラメーター値である。に確実に設定されたままになります-1an_intの実装に比較を含めるかどうかに応じてoperator==、それ自体がのメンバーと同等に比較されるものと同等に比較MyStruct1される場合とされない場合があります。さらに、一時的なものを作成することは、既存のメンバーを一時的なものにコピーし、比較後に破棄するだけなので、非常に非効率的な操作になる可能性があります。(もちろん、コンストラクターを作成するか、のデフォルト値を削除することで、比較のためにsが暗黙的に作成されるのを防ぐことができます。)MyStruct2MyStruct1my_struct_2MyStruct1my_struct2MyStruct1explicitan_int

メンバーの実装

MyStruct1からの暗黙の構築を避けたい場合はMyStruct2、比較演算子をメンバー関数にします。

struct MyStruct1
{
    ...
    bool operator==(const MyStruct1& rhs) const
    {
        return tie() == rhs.tie(); // or another approach as above
    }
};

constキーワード(メンバーの実装にのみ必要)は、オブジェクトを比較してもオブジェクトは変更されないため、オブジェクトで許可できることをコンパイラーに通知することに注意してくださいconst

目に見える表現の比較

必要な種類の比較を取得する最も簡単な方法は、次の場合があります...

    return lhs.to_string() == rhs.to_string();

...これもしばしば非常に高価です-それらstringはただ捨てられるために痛々しいほど作成されました!浮動小数点値を持つ型の場合、表示される表現を比較すると、表示される桁数によって、比較中にほぼ等しい値が等しいものとして扱われる許容範囲が決まります。

于 2011-04-21T06:43:15.377 に答える
16

operator ==forを明示的に定義する必要がありますMyStruct1

struct MyStruct1 {
  bool operator == (const MyStruct1 &rhs) const
  { /* your logic for comparision between "*this" and "rhs" */ }
};

== 比較は、そのような 2 つのオブジェクトに対して有効になりました。

于 2011-04-21T06:25:42.633 に答える
2

デフォルトでは、構造体には==演算子がありません。独自の実装を作成する必要があります。

bool MyStruct1::operator==(const MyStruct1 &other) const {
    ...  // Compare the values, and return a bool result.
  }
于 2011-04-21T06:42:19.760 に答える
1

C または C++ の構造体では比較が機能しません。代わりにフィールドで比較してください。

于 2011-04-21T06:25:10.440 に答える
0

デフォルトでは、 == 演算子はプリミティブに対してのみ機能します。コードを機能させるには、構造体の == 演算子をオーバーロードする必要があります。

于 2011-04-21T06:25:21.510 に答える
0

構造体の比較演算子を記述していないためです。コンパイラはそれを生成しないため、比較が必要な場合は自分で作成する必要があります。

于 2011-04-21T06:25:54.880 に答える