2012-08-05 8 views
38

デフォルトの移動セマンティクスをサポートするタイプTを考えてみましょう。また、以下の機能を考慮してください。古いC++ 03ではコピーから回避する関数から値を返すときにstd :: move()を使用する

T f() { 
    T t; 
    return t; 
} 

T o = f(); 

を、いくつかの非最適コンパイラは、二回「戻りオブジェクト」用とoのための1つをコピーコンストラクタを呼ぶかもしれません。

tf()はlvalueであるため、これらのコンパイラは以前と同じようにコピーコンストラクタを1回呼び出してから、oの移動コンストラクタを呼び出すことがあります。

最初の「余分なコピー」を回避する唯一の方法は、返信時にtを移動することです。

return文のローカル変数は、コピーの省略の対象であるときはいつでも、それは­ FE ­ rence再右辺値にバインド
T f() { 
    T t; 
    return std::move(t); 
} 
+0

[こちら](http://stackoverflow.com/questions/9827183/why-am-i-allowed-to-copy-unique-ptr)も同様の質問 –

+0

[関連FAQ] (http://stackoverflow.com/a/11540204/252000) – fredoverflow

答えて

42

号、ひいてはreturn t;コンストラクタであるかをに関してあなたの例ではreturn std::move(t);と同じです適格。

ただし、return std::move(t);は、のコピーエリジョンを実行しないようにしていますが、return t;そうではないので、後者が好ましいスタイルです。コルクのための@Johannesのおかげで、­ rect ­イオン。もしコピーエリシジョンが起これば、ムービングコンストラクションが使用されているかどうかの疑問が疑問点になります。

標準で12.8(31,32)を参照してください。

Tがアクセス可能で著作が、削除、移動、コンストラクタを持っている場合は移動コンストラクタが最初に考えなければならないので、その後return t;は、­山をcomポートしないことにも注意してください。あなたはそれを動作させるためにreturn static_cast<T&>(t);のEF ­ fectに何かを言う必要があるだろう:

T f() 
{ 
    T t; 
    return t;     // most likely elided entirely 
    return std::move(t);  // uses T::T(T &&) if defined; error if deleted or inaccessible 
    return static_cast<T&>(t) // uses T::T(T const &) 
} 
+0

ありがとうございます。移動コンストラクタへの呼び出しの回数はどうですか?これは、いくつかのC++ 03準拠のコンパイラを使ってコンストラクタをコピーする呼び出しの数を2にすることはできますか? – Martin

+12

※同一ではありません。移動コンストラクターを呼び出すことができるかどうかは同じです。 'return std :: move(t);'を書くと、コンパイラが何をしているのかわからない場合には、moveコンストラクタを呼び出さなければなりません。 'return t;'と書くと、副作用があっても移動コンストラクタ呼び出しを省略することができます。 –

+0

@ JohannesSchaub-litb:良い点、それを編集させてください。 –

3

[OK]を、私はこれにコメントをドロップしたいと思います。この質問(と答え)は、return文にstd::moveを指定する必要はないと私に信じさせました。しかし、自分のコードを扱いながら、私はちょっと別のレッスンと思っていました。

私は一時的なものを返し、それを返すだけの機能を持っています(実際には特殊化です)。 (一般的な関数テンプレートは他の処理を行いますが、特殊化はID演算を行います)。

ここで、このバージョンでは、コピーコンストラクタAが返されます。私は

Leaf_t make(A &&a) { 
    return std::move(a); 
} 

にreturn文を変更する場合は、Aの移動コンストラクタが呼び出されると、私はそこにいくつかの最適化を行うことができます。

あなたの質問に100%一致していない可能性があります。しかし、return std::move(..)が決して必要でないと考えるのは間違いです。私はそう思っていました。もう何もありません;-)

+0

これは元の質問とは異なります。元の質問は、xがローカル変数であるreturn xについてでした。 xが局所変数であるとき、xが局所であることを知っているので、コンパイラは戻り値内でrをrvalueとして扱うので、xがより良いです。 xが参照の場合、コンパイラは特別な扱いをしません。あなたの例の変数 "a"は "A&"なので、moveを使って "A &&"に変更する必要があります。 –

8

いいえ、直接return t;です。ケースクラスT

は削除されませんコンストラクタを移動している、と予告tは、それがreturn std::move(t);がするよう構造に返されたオブジェクトを移動return tは、コピーの省略の対象であることをローカル変数です。しかし、return t;は引き続きelisionのコピー/移動の対象となるため、構造を省略することがありますが、return std::move(t)は常にムーブコンストラクタを使用して戻り値を作成します。

クラスTの移動コンストラクタは削除されていますが、コピーコンストラクタが使用可能な場合、return std::move(t);はコンパイルされず、return t;はまだコピーコンストラクタを使用してコンパイルされます。上記の@Kerrekとは異なり、tは右辺値参照にバインドされていません。コピーエリートの対象となる戻り値には、2段階のオーバーロード解決があります。最初に移動してからコピーし、移動とコピーの両方が省略される可能性があります。 return式が、あなたはまだコピーを避けたい(あなたは非ローカル変数または左辺値式が戻ってきている可能性が高い)左辺値とコピーの省略の対象とされていない場合

class T 
{ 
public: 
    T() = default; 
    T (T&& t) = delete; 
    T (const T& t) = default; 
}; 

T foo() 
{ 
    T t; 
    return t;     // OK: copied, possibly elided 
    return std::move(t);  // error: move constructor deleted 
    return static_cast<T&>(t); // OK: copied, never elided 
} 

std::moveは有用であろう。しかし、ベスト・プラクティスは、コピー・エリジョンを起こすことが可能であることを覚えておいてください。

class T 
{ 
public: 
    T() = default; 
    T (T&& t) = default; 
    T (const T& t) = default; 
}; 

T bar(bool k) 
{ 
    T a, b; 
    return k ? a : b;   // lvalue expression, copied 
    return std::move(k ? a : b); // moved 
    if (k) 
     return a;    // moved, and possibly elided 
    else 
     return b;    // moved, and possibly elided 
} 

12.8(32)は、プロセスを記述しています。コピー操作のエリジオンの基準が満たされているか、ソースオブジェクトが関数のパラメータであるという事実のために保存満たされるであろう、そして物体があると

12.8 [class.copy]

32コピーは左辺値で指定され、コピーのコンストラクタを選択するオーバーロードの解像度は、オブジェクトが右辺値によって指定されたかのようにまず実行されます。オーバーロードの解決が失敗した場合、または選択されたコンストラクタの最初のパラメータの型がオブジェクトの型(おそらくcv修飾されたもの)への参照値でない場合は、オブジェクトを左辺値と見なして再度オーバーロードの解決が行われます。 [注:この2段階の過負荷解析は、コピーエリミッションが発生するかどうかに関係なく実行する必要があります。 elisionが実行されなかった場合に呼び出されるコンストラクタを決定し、選択されたコンストラクタは、呼び出しが省略されてもアクセス可能でなければなりません。 -end note]

関連する問題