32

代入演算子は、メンバー関数を使用してオーバーロードできますが、非メンバー関数は使用できませんfriend

class Test
{
    int a;
public:
    Test(int x)
        :a(x)
    {}
    friend Test& operator=(Test &obj1, Test &obj2);
};

Test& operator=(Test &obj1, Test &obj2)//Not implemented fully. just for test.
{
    return obj1;
}

次のエラーが発生します。

エラー C2801: 'operator =' は非静的メンバーでなければなりません

friend代入演算子のオーバーロードに関数を使用できないのはなぜですか? コンパイラは、+=-=usingなどの他の演算子をオーバーロードすることを許可していfriendます。をサポートする際の固有の問題/制限は何operator=ですか?

4

9 に答える 9

38

まず、これは特にフレンドとして実装されているオペレーターとは何の関係もないことに注意してください。実際には、コピー代入をメンバー関数として、または非メンバー (スタンドアロン) 関数として実装することが重要です。そのスタンドアロン関数がフレンドになるかどうかはまったく関係ありません。クラス内で何にアクセスしたいかによって、そうなるかもしれませんし、そうでないかもしれません。

さて、この質問に対する答えは、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++ 言語の観点からは、メンバー関数と非メンバー関数の関係であり、上記の理由により、コピー代入の非メンバー オーバーロードは完全に禁止されています。

于 2010-10-14T13:51:53.463 に答える
31

コンパイラによって提供されるデフォルトoperator=(メンバーごとのコピー) が常に優先されるためです。つまり、あなたの友人operator=は決して呼び出されません。

編集:この回答は

= operator をサポートする際の固有の問題/制限は何ですか?

質問の部分。ここでの他の回答は、あなたができないという標準の部分を引用していますが、これが標準のその部分がそのように書かれた理由である可能性が最も高いです。

于 2010-10-14T13:36:02.157 に答える
8

$13.5.3 - 「代入演算子は、厳密に 1 つのパラメーターを持つ非静的メンバー関数によって実装されるものとします。コピー代入演算子 operator= は、ユーザーによって宣言されていない場合、クラスに対して暗黙的に宣言されるため (12.8)、基底クラスの代入演算子は、派生クラスのコピー代入演算子によって常に隠されています。」

于 2010-10-14T13:40:00.817 に答える
7

メンバーでなければならないオペレーターがいくつかあるためです。これらの演算子は次のとおりです。
operator[]
operator=
operator()
operator->

などの型変換演算子operator int

正確に operator = がメンバーでなければならない理由を説明できるかもしれませんが、その議論はリスト内の他のものには適用できないため、「なぜ」に対する答えは「ただの理由」であると私は信じています。

HTH

于 2010-10-14T13:38:57.613 に答える
3

operator=自分で宣言しない場合にコンパイラが提供する特別なメンバー関数です。この特別なステータスのためoperator=、メンバー関数である必要があるため、コンパイラによって生成されoperator=たメンバーとユーザーによって宣言されたフレンドの両方が存在する可能性はなくoperator=、2 つから選択する可能性はありません。

于 2010-10-14T13:42:06.243 に答える
1

代入演算子のオーバーロードにフレンド関数を使用できないのはなぜですか?

簡単な答え:という理由だけで

やや長い答え: これが構文の修正方法です。いくつかの演算子はメンバー関数でなければなりません。代入演算子は、

于 2010-10-14T13:40:57.780 に答える
0

の意図はoperator=、現在のオブジェクトへの代入操作です。その場合、LHS または左辺値は同じ型のオブジェクトです。

LHS が整数またはその他の型である場合を考えてみましょう。operator int()これは、または対応するoperator T()関数によって処理されるケースです。したがって、LHS の型は既に定義されていますが、非メンバーoperator=関数はこれに違反する可能性があります。

したがって、それは避けられます。

于 2012-07-04T13:16:16.570 に答える