web-dev-qa-db-ja.com

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

代入演算子は、メンバー関数を使用してオーバーロードできますが、非メンバー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: '演算子='は非静的メンバーである必要があります

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

30
bjskishore123

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

編集:この答えは答えています

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

質問の一部。ここでの他の回答は、あなたがそれを行うことができないと言っている標準の部分を引用していますが、これはおそらくなぜ標準のその部分はそのように書かれています。

29
Billy ONeal

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

さて、この質問への答えは、D&Eブック( 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++ではコピー代入演算子をスタンドアロン関数として宣言することはできません。

特にfriend関数を使用する特定の例では、演算子はクラス定義内で非常に早い段階で宣言されていることは事実です(友達がどのように宣言されるか)。したがって、あなたの場合、コンパイラーはもちろん、オペレーターの存在をすぐに知ることができます。ただし、C++言語の観点からは、一般的な問題はフレンド関数とはまったく関係ありません。 C++言語の観点からは、メンバー関数と非メンバー関数の違いであり、上記の理由により、コピー代入の非メンバーオーバーロードは完全に禁止されています。

34
AnT

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

8
Chubsdad

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

_operator int_のような型変換演算子。

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

HTH

7
Armen Tsirunyan

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

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

簡単な答え:理由だけで

もう少し長い答え:それが構文が修正された方法です。いくつかの演算子はメンバー関数である必要があります。代入演算子は、の1つです。

1
sbi

'='の暗黙の演算子オーバーロード関数がクラスにあるため浅いコピー。したがって、フレンド関数を使用してオーバーロードした場合でも呼び出すことはできませんオーバーロードされたフレンド関数ではなく、暗黙の浅いコピーメソッドを呼び出すためです。

0
Nitin Gautam

_operator=_の目的は、現在のオブジェクトへの割り当て操作です。その場合、LHSまたは左辺値は同じタイプのオブジェクトです。

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

したがって、それは回避されます。

この投稿はC++ 11に適用されます

なぜ誰かが非会員_operator=_を欲しがるのですか?さて、メンバー_operator=_を使用すると、次のコードが可能になります。

_Test const &ref = ( Test() = something ); 
_

ぶら下がっている参照を作成します。非メンバーオペレーターはこれを修正します:

_Test& operator=(Test &obj1, Test obj2)
_

これで、prvalue Test()は_obj1_にバインドできなくなります。実際、このシグニチャは、ぶら下がっている参照を返さないことを強制します(もちろん、参照が提供されていない限り)。関数は、左辺値で呼び出されることを強制するため、常に「有効な」左辺値を返します。

ただし、C++ 11では、メンバー関数を左辺値でのみ呼び出すことができるように指定する方法があるため、メンバー関数を作成することで同じ目標を達成できます。

_Test &operator=(Test obj2) &
//                        ^^^
_

これで、参照がぶら下がっている上記のコードはコンパイルに失敗します。


注意。 _operator=_は、値またはconst参照のいずれかで右側をとる必要があります。値による取得は、 コピーとスワップのイディオム を実装するときに役立ちます。これは、安全な(ただし必ずしも最速ではない)コピー代入演算子とムーブ代入演算子を簡単に記述できる手法です。

0
M.M