まず、これは特にフレンドとして実装されているオペレーターとは何の関係もないことに注意してください。実際には、コピー代入をメンバー関数として、または非メンバー (スタンドアロン) 関数として実装することが重要です。そのスタンドアロン関数がフレンドになるかどうかはまったく関係ありません。クラス内で何にアクセスしたいかによって、そうなるかもしれませんし、そうでないかもしれません。
さて、この質問に対する答えは、D&E ブック ( The Design and Evolution of C++ ) に記載されています。これは、コンパイラが常にクラスのメンバー コピー代入演算子を宣言/定義するためです (独自のメンバー コピー代入演算子を宣言しない場合)。
言語でコピー代入演算子をスタンドアロン (非メンバー) 関数として宣言することも許可されている場合、次のようになる可能性があります。
// Class definition
class SomeClass {
// No copy-assignment operator declared here
// so the compiler declares its own implicitly
...
};
SomeClass a, b;
void foo() {
a = b;
// The code here will use the compiler-declared copy-assignment for `SomeClass`
// because it doesn't know anything about any other copy-assignment operators
}
// Your standalone assignment operator
SomeClass& operator =(SomeClass& lhs, const SomeClass& rhs);
void bar() {
a = b;
// The code here will use your standalone copy-assigment for `SomeClass`
// and not the compiler-declared one
}
上記の例に見られるように、コピー代入のセマンティクスは、翻訳単位の途中で変更されます。つまり、スタンドアロン オペレーターの宣言の前に、コンパイラのバージョンが使用されます。宣言の後、あなたのバージョンが使用されます。プログラムの動作は、スタンドアロンのコピー代入演算子の宣言をどこに置くかによって異なります。
これは容認できないほど危険であると考えられていた (実際にそうである) ため、C++ ではコピー代入演算子をスタンドアロン関数として宣言することは許可されていません。
たまたまフレンド関数を具体的に使用している特定の例では、クラス定義内で演算子が非常に早い段階で宣言されていることは事実です(それがフレンドの宣言方法であるため)。したがって、あなたの場合、コンパイラーはもちろん、オペレーターの存在をすぐに認識します。ただし、C++ 言語の観点からは、一般的な問題はフレンド関数とはまったく関係ありません。C++ 言語の観点からは、メンバー関数と非メンバー関数の関係であり、上記の理由により、コピー代入の非メンバー オーバーロードは完全に禁止されています。