2017-01-07 6 views
11
#include <string> 
#include <vector> 

using namespace std; 

auto f() 
{ 
    vector<string> coll{ "hello" }; 

    // 
    // Must I use move(coll[0]) ? 
    // 
    return coll[0]; 
} 

int main() 
{ 
    auto s = f(); 
    DoSomething(s); 
} 

私にはちょうどreturn coll;の場合は、collが返品時に移転することが保証されています。テンポラリオブジェクトのサブオブジェクトは、返されて移動することが保証されていますか?

ただし、わかりません:coll[0]の返品はいつでも保証されますか?

更新:

#include <iostream> 

struct A 
{ 
    A() { std::cout << "constructed\n"; } 
    A(const A&) { std::cout << "copy-constructed\n"; } 
    A(A&&) { std::cout << "move-constructed\n"; } 
    ~A() { std::cout << "destructed\n"; } 
}; 

struct B 
{ 
    A a; 
}; 

A f() 
{ 
    B b; 
    return b.a; 
} 

int main() 
{ 
    f(); 
} 

GCC 6.2と3.8打ち鳴らす出力同じ:

コピー構築

を破壊構築

+2

"ならば、' coll'は返却時に移動することが保証されます。いいえ、そうではありません。コピーは省略されてもよく、その場合、移動はありません。 – juanchopanza

+1

あなたはf()の戻り値を使用していないので、移動させるものは何ですか? – Surt

+0

左辺値を移動できる条件は、コピーエリジョンと密接に関連しています(私の答えを参照してください)。 – juanchopanza

答えて

3

「暗黙のムーブ」ルールのクリーンな製剤は現在のワーキングペーパーの[class.copy.elision]/3にある次のコピーの初期コンテキストで

、移動操作は 代わりコピー操作に使用されるかもしれません。

  • return文で表現([のstmtの場合。リターン])は、その名(おそらく)括弧ID発現ある身体又は最も内側の機能またはラムダ式、又は

    パラメータ宣言節で宣言さ 自動記憶域期間を持つオブジェクトオブジェクトは右辺値で指定されたかのようにコピーするためのコンストラクタを選択する
  • [...]

オーバーロード解決は 最初の実行です。最初の のオーバーロード解決が失敗した場合、または実行されなかった場合、または選択されたコンストラクタの の最初のパラメータの型がオブジェクトの型(おそらくcv修飾された)への参照値 ではない場合、オーバーロードの解決は 左辺値としてのオブジェクト

b.acoll[0]いずれもID発現あります。したがって、暗黙的な移動はありません。あなたが移動を望むなら、あなたはそれを明示的に行う必要があります。

4

を破壊ローカルオブジェクトを返す場合、コピーや移動のいずれも移動好まれるべき、使用されるが、エリジオンをコピーします。これは、コピー・エリジョンとローカル・オブジェクトの移動を管理するルールが同じであるためです。代わりに、明示的に打ち鳴らす問題の

template<typename T> 
std::string make_string(T const& x) 
{ 
    std::ostringstream str; 
    str << x 
    return std::move(str.str()); // not recommended 
} 

最近のバージョンのようにstd::moveを使用して動きを強制すると、一時的なオブジェクトの防止を動かす警告

は[-Wpessimizing-移動]

エリジオンをコピーしますただし、コード内のの状況は異なるです。オブジェクト(std::string)、std::vector<>::operator[]を返すstd::ostringstream::str()とは異なり、参照はオブジェクトに変換する必要があります(autoは参照を削除するため)。この場合、実際のオブジェクトはデストラクタではない別のオブジェクトの一部であるため、コピーエリートは不可能であり、コピーを避けるにはstd::move()を使用する必要があります。

これらの考慮事項は、わからない場合はstd::move()を使用することを推奨しますが、上記の警告が警告の場合は削除してください。

+0

いいえgcc 6.2とclang 3.8のどちらも、私の元の例ではコピーエリッションはありません。私の更新を参照してください。 – xmllmx

関連する問題