演算子のオーバーロード:メンバー関数と非メンバー関数?


121

メンバー関数として宣言されたオーバーロードされた演算子は非対称であると読みましたthis。これは、パラメーターを1つしか持つことができず、自動的に渡される他のパラメーターがポインターであるためです。したがって、それらを比較する基準はありません。一方、オーバーロード演算子はとして宣言されfriendている対称我々は、同じタイプの2つの引数を渡し、したがって、それらを比較することができるからです。

私の質問は、ポインターの左辺値を参照と比較できるのに、なぜ友達が好まれるのですか?(非対称バージョンを使用すると、対称バージョンと同じ結果が得られます)なぜSTLアルゴリズムは対称バージョンのみを使用するのですか?


11
あなたの質問は本当に二項演算子についてのみです。すべてのオーバーロードされた演算子が単一のパラメーターに制限されるわけではありません。()演算子は、任意の数のパラメーターを取ることができます。一方、単項演算子はパラメーターを持つことができません。
Charles Salvia、2011年


4
これは、C ++ FAQで
Ben Voigt

回答:


148

演算子のオーバーロードされた関数をメンバー関数として定義すると、コンパイラーは式をs1 + s2に変換しs1.operator+(s2)ます。つまり、演算子のオーバーロードされたメンバー関数は、最初のオペランドで呼び出されます。これがメンバー関数の機能です!

しかし、最初のオペランドがクラスでない場合はどうなりますか?最初のオペランドがクラス型ではない演算子をオーバーロードしたい場合、大きな問題がありますdoubleしたがって、このように書くことはできません 10.0 + s2。ただし、演​​算子のオーバーロードされたメンバー関数をのような式に記述できますs1 + 10.0

この順序付けの問題を解決するために、メンバーfriendにアクセスする必要がある場合に、演算子の多重定義関数を定義しますprivateそれを作るfriend、それがプライベートメンバーにアクセスする必要がある場合のみです。それ以外の場合は、単純に非フレンド、非メンバー関数にしてカプセル化を改善してください!

class Sample
{
 public:
    Sample operator + (const Sample& op2); //works with s1 + s2
    Sample operator + (double op2); //works with s1 + 10.0

   //Make it `friend` only when it needs to access private members. 
   //Otherwise simply make it **non-friend non-member** function.
    friend Sample operator + (double op1, const Sample& op2); //works with 10.0 + s2
}

これらを読んでください:
オペランドの順序付けのわずかな問題
非メンバー関数がカプセル化を改善する方法


2
friendプライベートメンバーにアクセスする必要がある場合にのみ作成してください。また、アクセサを作成するのに飽きていない/退屈しているときに作成しますか?
badmaash

4
@Abhi:選択肢を選択してください:カプセル化とレイジー書き込みの習慣の改善!
Nawaz、2011年

6
@matthias、すべての演算子が可換ではありません。簡単な例はa/bです。
edA-qa mort-ora-y 2012

3
非メンバーオペレーターを必要friendとしないようにする一般的な方法は、操作割り当てオペレーター(ほぼ確実にパブリックメンバーになる)の観点からそれらを実装することです。たとえばT T::operator+=(const T &rhs)、メンバーとして定義してから、非メンバーをT operator(T lhs, const T &rhs)として定義できますreturn lhs += rhs;。非メンバー関数は、クラスと同じ名前空間で定義する必要があります。
エイドリアンマッカーシー

2
@ricky:しかし、lhsが(私のコメントにあるように)コピーである場合、lhsが変更されるという事実は重要ではありません。
エイドリアン・マッカーシー

20

friend演算子のオーバーロードとメンバー関数の演算子のオーバーロードは、グローバルな演算子のオーバーロードとメンバー関数の演算子のオーバーロードとの間で区別されるわけではありません。

グローバル演算子のオーバーロードを好む1つの理由は、クラス型が二項演算子の右側に現れる式を許可する場合です。例えば:

Foo f = 100;
int x = 10;
cout << x + f;

これは、グローバル演算子のオーバーロードがある場合にのみ機能します

Foo演算子+(int x、const Foo&f);

グローバル演算子オーバーロードは必ずしもfriend関数である必要はないことに注意してください。これは、のプライベートメンバーにアクセスする必要がある場合にのみ必要ですFooが、常にそうであるとは限りません。

いずれにせよ、Fooメンバー関数の演算子のオーバーロードのみがあった場合は、次のようになります。

class Foo
{
  ...
  Foo operator + (int x);
  ...
};

...その後、プラス演算子の左側Fooインスタンスが表示される式のみが可能になります。


3
メンバー関数とフレンド関数ではなく、メンバー関数と非メンバー関数を区別するための+1。今日は「グローバルスコープまたはネームスペーススコープ」と言います。
エイドリアンマッカーシー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.