2013-06-17 8 views
5
#include<iostream> 
using namespace std; 
class X 
{ 
    int i; 

    public: 
    X(int a=0) : i(a) {} 

    friend X operator+ (const X& left,const X&right); 

}; 
X operator+ (const X& left,const X&right) // Method 1 
{ 
    return X(left.i + right.i); 
} 

X operator+ (const X& left,const X&right) // Method 2 
{ 
    X temp(left.i + right.i); 
    return temp; 
} 

int main() 
{ 
    X a(2),b(3),c; 

    c=a+b; 

    c.print(); 
    return 0; 
} 

このコードでは、演算子+は2つの異なる方法でオーバーロードされています。C++での演算子オーバーロードのこれらのメソッドの相違点

私の質問これらの方法の違いは何ですか?また、使用するのがより実用的であると考えられるべきですか?

+1

C++ 11では、 'return {left.i + right.i}'は方法3で、同じことをやっています(些細な最適化の後も)。 – Yakk

答えて

6

コンパイラがこれら2つのバージョン間で異なるコードを生成するケースはありません。 2番目の方がもう少し冗長ですが、コンパイラはその場合に想定される余分なコピーを最適化することが許されていますが、私はそのコンパイルをしないコンパイラは認識していません。

これは、マイクロ最適化です。最も明確なコードを書くと、それが最終的なポイントになります。これらの演算子のいずれかを書きますが、+=と一緒に慣用のバージョンを記述しないでください:

X& operator+=(const X&right) { i += right.i; return *this; } 
X operator+(X left, const X& right) { return left += right; } 
+0

+1。 @MichaelSmith:私はあなたがこの答えを選ぶべきだと思っています。あなたのオペレータを設計する方法について正しい提案をしてくれます。 –

+0

あなたのコードを 'constexpr'にする場合は、' operator + 'を書いて、それに関して' operator + = 'と書いてください。 – CTMacUser

3

これらの2つの方法には違いはなく、その意図を最もよく伝える方法を使用する必要があります。 コピーの省略

段落12.8/31は、指定:特定の基準が満たされた場合

を、実装がコンストラクタはのために選択された場合でも、クラス オブジェクトのコピー/移動する構成を省略することが許可されていますコピー/移動操作および/またはオブジェクトのデストラクタ には副作用があります。そのような場合、実装は省略されたコピー/移動のソースとターゲットを同じオブジェクトを参照する単純に2つの異なる方法として処理し、そのオブジェクトの破棄は2つのオブジェクトが最適化なしで破壊されました。 コピーの省略と呼ばれるコピー/移動操作のこのエリジオンは、( は複数のコピーを排除するために組み合わせることができる)は、以下の状況で許可されている:

- クラス戻り型と機能にreturnステートメントで、式が関数戻り型と同じcv-unqualified 型を持つ 不揮発性自動オブジェクト(関数またはcatch節パラメータ以外)の名前である場合、コピー/移動操作を省略することができます 自動オブジェクトを関数の戻り値に直接組み込むことによって

- [...]

あなたが見ることができるように、一時的に作成し、自動記憶域期間を持つローカルオブジェクトを命名ID-表現両方がコピーの省略のために修飾。

さらに、ローカルのtempの値の(この場合は一時的です)を関数から返すために処理します。パラグラフ12.8 C++ 11標準指定の/ 32:

コピー操作のエリジオンの基準が満たされているか、ソース オブジェクトは、関数のパラメータがあるという事実のために保存満たされるであろう

、及びコピーするオブジェクトを左辺値で指定します。オーバーロードの解像度を に設定します。コピー先のコンストラクタは、オブジェクトが右辺値で指定されたかのように最初に実行されます。 [...あなたから移動することはできませんので、これは移動のセマンティクスを阻害する、C++ 11では

const X operator + (const X& left, const X&right) 
// ^^^^^ 
// Don't use this! 

:]このため

を、私は強く、戻り値の型からconst資格を取り除くを示唆していますconstオブジェクトは、たとえそれが右辺値であっても、手短に言えば、移動コンストラクタXが存在していればそれが選択されず、コピーコンストラクタが呼び出されます。

+0

は "RVO"のコンセプトを使用した最初のものではありませんか?もし私が間違っていれば私を正す? –

+0

おそらく、彼は戻り値オブジェクトではなく、メソッドをconstにすることを意図していました。 –

+1

@ T.E.D:メンバ関数ではないので、 'const'にすることはできません... –

2

違いは、方法1では、「戻り値の最適化」という概念を使用しています。

方法1:コンパイラはあなたがそれを返すこと以外に作成されたオブジェクトのための使用を持っていないことを認識し

。コンパイラはこれを利用して、 "この値が返されるはずの場所に直接オブジェクトを構築します"。ここでは、通常のコンストラクタ呼び出しは1つだけ必要です(コピーコンストラクタは不要です)。ローカルオブジェクトを実際に作成したことがないため、デストラクタコールはありません。これはより効率的です。


は方法2:

まず一時オブジェクトtempという名前が作成されます。コピーコンストラクタはtempをコピー先の戻り値の場所にコピーします。 そして、デストラクターはスコープの終わりにtempのために呼び出されます。

最終的には方法1が効率的ですが、これはコンパイラに依存する機能です。

+4

非現代的で現代的なコンパイラは、両方のバージョンで全く同じコードを発行します。また、移動のセマンティクスについて忘れています。 – Griwes

+0

ですが、方法2では、コンストラクタ呼び出しを含む一時オブジェクトが作成されます。 –

+0

おそらく私は何かが不足しているかもしれませんが、なぜオプティマイザがこれらの2つのルーチンを異なる方法で扱うのか分かりません。彼らは、いずれの目的にも使用されていないが値を返す、その場で作成されたオブジェクトを返す。 –

0

第2の実施はNRVの最適化の原因となります。 Stan LippmanはNRVの最適化には明示的なコピーコンストラクタが必要だと言っていましたが、ここではクラスXは単純なのでNRVには明示的なコピーコンストラクタが必要とは思われません。

関連する問題