次の構造体の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'の左側のオペランドをとる演算子が見つかりません(または受け入れ可能な変換がありません)
どうして?
C++では、struct
sにはデフォルトで生成される比較演算子がありません。独自に作成する必要があります。
bool operator==(const MyStruct1& lhs, const MyStruct1& rhs)
{
return /* your comparison code goes here */
}
他の人が言ったように、あなた自身で比較関数を実装する必要があります。
コンパイラーに明白な/単純な(?)実装を生成するように依頼する方法が提案されています: here を参照してください。
これを標準化していないことはC++には少し役に立たないように思えるかもしれませんが、多くの場合、構造体/クラスには比較から除外するデータメンバーがあります(例:カウンター、キャッシュ結果、コンテナー容量、最後の操作の成功/エラーコード、カーソル)、およびする意思決定以下を含むがこれらに限定されない無数のものについて:
int
メンバーを比較すると、等しくないオブジェクトの99%が非常に迅速に削除される可能性がありますが、map<string,string>
メンバーは多くの場合、同一のエントリを持ち、比較に比較的コストがかかる場合があります-実行時に値がロードされると、プログラマーはコンパイラーの洞察を得ることができますできませんvector
、list
)、比較する前にインプレースでソートするか、比較するたびに一時メモリをソートするために余分なメモリを使用するかunion
のメンバー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メンバータイバージョン ここ )
自立型とメンバー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
は、それ自体がMyStruct2
のMyStruct1
メンバーと等しいmy_struct_2
と等しいかどうかを比較します。さらに、一時的なMyStruct1
の作成は、既存のmy_struct2
メンバーを一時的なものにコピーし、比較後に破棄するだけなので、非常に効率の悪い操作になる可能性があります。 (もちろん、コンストラクターをexplicit
にするか、MyStruct1
のデフォルト値を削除することにより、比較のためにan_int
sのこの暗黙的な構築を防ぐことができます。)
メンバーの実装
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
sはただ捨てられるために痛々しいほど作成されました!浮動小数点値を持つ型の場合、可視表現を比較することは、表示される桁数が、比較中にほぼ等しい値が等しいものとして扱われる許容範囲を決定することを意味します。
operator ==
に対してMyStruct1
を明示的に定義する必要があります。
struct MyStruct1 {
bool operator == (const MyStruct1 &rhs) const
{ /* your logic for comparision between "*this" and "rhs" */ }
};
現在、==比較は、このような2つのオブジェクトに対して有効です。
比較は、CまたはC++の構造体では機能しません。代わりにフィールドで比較します。
C++ 20以降では、 デフォルトのスリーウェイ)を宣言することにより、クラスにデフォルトの比較演算子(==
、<=
など)の完全なセットを追加できるようになるはずです。比較演算子 (「宇宙船」演算子)、次のように:
struct Point {
int x;
int y;
auto operator<=>(const Point&) const = default;
};
準拠するC++ 20コンパイラーでは、MyStruct2の定義に互換性があると仮定すると、MyStruct1とMyStruct2にその行を追加するだけで同等の比較が可能になります。
デフォルトでは、構造体には==
演算子がありません。独自の実装を作成する必要があります。
bool MyStruct1::operator==(const MyStruct1 &other) const {
... // Compare the values, and return a bool result.
}
すぐに使用できる==演算子は、プリミティブに対してのみ機能します。コードを機能させるには、構造体の==演算子をオーバーロードする必要があります。
構造体に比較演算子を書いていないからです。コンパイラーはそれを生成しないため、比較が必要な場合は、自分で作成する必要があります。