web-dev-qa-db-ja.com

C ++で構造体の比較中に==演算子が見つかりません

次の構造体の2つのインスタンスを比較すると、エラーが表示されます。

struct MyStruct1 {
    Position(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'の左側のオペランドをとる演算子が見つかりません(または受け入れ可能な変換がありません)

どうして?

86
Jonathan

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

bool operator==(const MyStruct1& lhs, const MyStruct1& rhs)
{
    return /* your comparison code goes here */
}
116

他の人が言ったように、あなた自身で比較関数を実装する必要があります。

コンパイラーに明白な/単純な(?)実装を生成するように依頼する方法が提案されています: here を参照してください。

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

  • どのフィールドを最初に比較するか、例えば特定のintメンバーを比較すると、等しくないオブジェクトの99%が非常に迅速に削除される可能性がありますが、map<string,string>メンバーは多くの場合、同一のエントリを持ち、比較に比較的コストがかかる場合があります-実行時に値がロードされると、プログラマーはコンパイラーの洞察を得ることができますできません
  • 文字列の比較:大文字と小文字の区別、空白と区切り文字の等価性、エスケープ規則...
  • float/doubleを比較するときの精度
  • naN浮動小数点値を等しいと見なすべきかどうか
  • ポインターまたはデータへのポインターの比較(後者の場合、ポインターが配列を指しているかどうか、および比較が必要なオブジェクト/バイトの数を知る方法)
  • 未ソートのコンテナを比較するときに順序が重要かどうか(例:vectorlist)、比較する前にインプレースでソートするか、比較するたびに一時メモリをソートするために余分なメモリを使用するか
  • 比較する有効な値を現在保持している配列要素の数(サイズはどこかにありますか、それともセンチネルですか?)
  • 比較するunionのメンバー
  • 正規化:たとえば、日付の種類によって範囲外の日または月が許可される場合や、有理数/分数オブジェクトに6/8があり、別のオブジェクトに3/4erが含まれる場合があります。個別の正規化ステップで遅延的に;比較する前に正規化をトリガーするかどうかを決定する必要がある場合があります
  • 弱いポインターが有効でない場合の対処方法
  • operator==自体を実装していないメンバーとベースを処理する方法(ただし、compare()またはoperator<またはstr()またはgetterがある場合があります...)
  • 他のスレッドが更新する可能性のあるデータの読み取り/比較中にどのロックを取得する必要があるか

ですから、特定の構造にとって比較が何を意味するかを明確に考えるまでは、エラーが発生するのはいいことで、コンパイルせずに意味のある結果が得られるのではなく実行時

とは言っても、C++で「ナイーブな」メンバーごとの==テストwasを決定したときにbool operator==() const = default;を言ってくれたらいいと思います。 !=についても同じです。複数のメンバー/ベースを考えると、「デフォルト」の<<=>、および>=の実装は望みがないように思われます。メンバー、アクセシビリティによるグループ化、依存使用前の構築/破壊)。より広く使用するには、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を使用して、フィールドへの参照のタプルを目的の比較順序で作成します。 here から私の例を一般化します:

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の準備を「所有」する(つまり、企業およびサードパーティのライブラリを使用してファクターを編集できる)場合、比較できるようにしたいクラスに「tie」メンバー関数を追加する方が良い:

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を検索)をお勧めします。何らかの理由で不適切な場合は、サポートマクロ (online)

#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がコンストラクターのデフォルトパラメーター値-1に設定されたままになります。 an_intの実装にoperator==比較を含めるかどうかに応じて、MyStruct1は、それ自体がMyStruct2MyStruct1メンバーと等しいmy_struct_2と等しいかどうかを比較します。さらに、一時的なMyStruct1の作成は、既存のmy_struct2メンバーを一時的なものにコピーし、比較後に破棄するだけなので、非常に効率の悪い操作になる可能性があります。 (もちろん、コンストラクターをexplicitにするか、MyStruct1のデフォルト値を削除することにより、比較のためにan_intsのこの暗黙的な構築を防ぐことができます。)

メンバーの実装

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

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

81
Tony Delroy

operator ==に対してMyStruct1を明示的に定義する必要があります。

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

現在、==比較は、このような2つのオブジェクトに対して有効です。

14
iammilind

比較は、CまたはC++の構造体では機能しません。代わりにフィールドで比較します。

2
Rafe Kettler

C++ 20以降では、 デフォルトのスリーウェイ)を宣言することにより、クラスにデフォルトの比較演算子(==<=など)の完全なセットを追加できるようになるはずです。比較演算子 (「宇宙船」演算子)、次のように:

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

準拠するC++ 20コンパイラーでは、MyStruct2の定義に互換性があると仮定すると、MyStruct1とMyStruct2にその行を追加するだけで同等の比較が可能になります。

2
Joe Lee

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

bool MyStruct1::operator==(const MyStruct1 &other) const {
    ...  // Compare the values, and return a bool result.
  }
1
Jonathan

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

0
Babak Naffas

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

0
user500944