C ++ 20は、デフォルトの比較、別名「宇宙船」をoperator<=>
導入しました。これにより、コンパイラが生成した<
/ <=
/ ==
/ !=
/ >=
/および/または>
演算子を明白な/ naive(?)実装で要求できます...
auto operator<=>(const MyClass&) const = default;
...しかし、より複雑な状況に合わせてカスタマイズできます(以下で説明します)。正当化と議論を含む言語提案については、こちらをご覧ください。この回答は、C ++ 17以前、およびoperator<=>
... の実装をカスタマイズする必要がある場合の洞察に関連しています。
C ++がこれを以前に標準化していないと少し役に立たないように見えるかもしれませんが、多くの場合、構造体/クラスには比較から除外するデータメンバーがあります(たとえば、カウンター、キャッシュされた結果、コンテナー容量、最後の操作の成功/エラーコード、カーソル)。次のものを含むがこれらに限定されない無数の事柄について行う決定と同様に:
- 最初に比較するフィールド。たとえば、特定の
int
メンバーを比較すると、map<string,string>
一致しないオブジェクトの99%がすぐに削除される可能性があります。一方、メンバーは同じエントリを持ち、比較するのに比較的コストがかかる場合があります。値が実行時に読み込まれると、プログラマーは、コンパイラはおそらくできません
- 文字列の比較:大文字と小文字の区別、空白とセパレータの同等性、エスケープ規則...
- float / doubleを比較するときの精度
- NaN浮動小数点値を等しいと見なすべきかどうか
- ポインターまたはポイントされたデータの比較(後者の場合、ポインターが配列に対するものかどうか、および比較が必要なオブジェクト/バイトの数をどのようにして知るか)
- 順序が重要かどうかソートされていないコンテナを比較する場合(例えば
vector
、list
)、そしてそれは、ソート一時に比較が行われるたびに余分なメモリを使用して対比較する前にその場でそれらをソートする大丈夫ですかもし
- 比較する必要のある有効な値を現在保持している配列要素の数(どこかにサイズがあるのか、センチネルがあるのか)
union
比較するaのメンバー
- 正規化:たとえば、日付タイプは範囲外の日または月を許可する場合や、有理/小数オブジェクトは6/8を持ち、別のオブジェクトは3/4を持っている場合があります。独立した正規化ステップを遅延して; 比較の前に正規化をトリガーするかどうかを決定する必要がある場合があります
- 弱いポインタが有効でない場合の対処法
operator==
自身を実装しないメンバーとベースの処理方法(またはゲッターがあるcompare()
かoperator<
、str()
または持っている可能性があります...)
- 他のスレッドが更新する可能性のあるデータの読み取り/比較中に取得する必要があるロック
したがって、特定の構造に対して比較が何を意味するかを明示的に考えて、コンパイル時に実行時に意味のある結果が得られないよりも、エラーが発生するのはいいことです。
C ++は、あなたが言わせた場合、それはいいだろう、と述べているすべてのbool operator==() const = default;
あなたが「ナイーブ」メンバーごとにメンバーを決めたのだとき==
テストはした OK。も同じです!=
。与えられた複数のメンバーは、/塩基は、「デフォルト」<
、<=
、>
、および>=
実装は絶望的ものの思える-宣言の可能性はなく、望んでいたことで、グループ化、塩基はメンバーの前に必ずいる(メンバーの順序のための要請を矛盾与えられているものであることが非常に低いのオーダーに基づいてカスケード依存性使用前のアクセシビリティ、構築/破壊)。より広く役立つためには、C ++は選択をガイドする新しいデータメンバー/ベースアノテーションシステムを必要とするでしょう。それ'
等値演算子の一般的な実装
もっともらしい実装
それはだ可能性が合理的かつ効率的な実装が可能であろう。
inline bool operator==(const MyStruct1& lhs, const MyStruct1& rhs)
{
return lhs.my_struct2 == rhs.my_struct2 &&
lhs.an_int == rhs.an_int;
}
これにはoperator==
for 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...
比較したいクラス、特にC ++ 14がreturn
ステートメントから関数の戻り値の型を推測できるように準備している(つまり、企業およびサードパーティのライブラリの要素を編集できる)場合は、多くの場合、 "メンバー関数を、比較したいクラスに結び付けます。
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
、コンストラクタのデフォルトパラメータ値に設定したままにします-1
。あなたが含まれているか否かに応じて、an_int
あなたの実装で比較operator==
、MyStruct1
かもしれないのかは等しいとしない場合がありますMyStruct2
自体がと等しいことMyStruct1
のmy_struct_2
メンバーを!さらに、MyStruct1
既存のmy_struct2
メンバーを一時ファイルにコピーし、比較後に破棄するだけなので、一時ファイルの作成は非常に非効率的な操作になる可能性があります。(もちろん、コンストラクターをMyStruct1
作成するexplicit
か、のデフォルト値を削除することで、この暗黙的なsの構築を回避して比較できますan_int
。)
メンバーの実装
MyStruct1
からのaの暗黙的な構築を回避する場合は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
、捨てられるだけのために痛々しく作成されています。浮動小数点値を持つ型の場合、可視表現を比較することは、表示される桁数が許容差を決定することを意味し、その範囲内でほぼ等しい値が比較中に等しいものとして扱われます。
struct
s を比較して等しいかどうかを知るのですか?簡単な方法が必要な場合はmemcmp
、構造体にポインタが含まれていない場合が常にあります。