更新:どうやら、このテーマに関する WG21 の論文があり、私の投稿よりもはるかに詳細です。このトピックに関する彼の CppCon 講演を見逃して、同じような結論に達したことに驚いています。
C++ 演算子のオーバーロードの原則の 1 つは、オーバーロードされた演算子の動作が基本型の対応するものと似ている必要があるということです。ほとんどの場合、私たちはまともな仕事をしています。しかし、代入演算子を定義する方法には驚きがあります。
次のコード スニペットを検討してください:
1 = 2; // error: lvalue required as left operand of assignment
ほとんどのプログラミング言語ではまったくナンセンスです。実際、これに満足しているコンパイラはありません
ただし、クラス タイプの場合は、テンポラリに割り当ててもまったく問題ありません:
struct S {};
int main() {
S{} = S{};
}
その理由は、代入演算子の型シグネチャが、コンパイラで生成されたものであるか、手動で定義したものであるかにかかわらず、右辺値型を除外しないためです:
struct S {
// Perfectly happy with `*this` being both lvalue or rvalue
auto operator=(const S& other) -> S&;
auto operator=(S&& other) -> S&;
};
簡単な修正は、割り当ての左辺値参照のオーバーロードを定義することだけです:
struct S {
auto operator=(const S& other) & -> S&;
auto operator=(S& other) & -> S&;
};
int main() {
S{} = S{};
}
この場合、3 つの主要なコンパイラはすべて、オーバーロードの解決が失敗した場合に通常発生するのと同じように、やや神秘的なエラー メッセージを作成しましたが、少なくともコンパイルは行われません。
この問題は重要ですか?
私にとって、この懸念は深刻な問題というよりも、理論上の煩わしさです。うっかり右辺値を割り当ててしまうベテランのプログラマーを想像することはできません。これまでにプログラミング言語に触れたことがない場合、まったくの初心者にとって問題になる可能性があります。また、ダブルイコールが必要なときにシングルイコールを書くと、初心者を混乱させる可能性があります。 const
まで宣言するベスト プラクティスに従っている場合 可能な限り、代入の左側は const 値または一時値のいずれかになります。このようなエラーは、割り当ての左辺値参照を修飾することでキャッチできます。
それでも、0 のルールに従い、代入演算子に触れないことをお勧めします。ただし、割り当てを定義する必要がある場合は、左辺値参照のみをオーバーロードすることを検討してください。 C++ がエポックのようなものを取得した場合、標準ライブラリ型の代入演算子に参照修飾子を追加できる可能性があります (例:std::string
)。 .