2016-03-11 10 views
16

タプル/タイの場合の戻り値の最適化を見ています。私が観察した動作は期待通りではありません。以下の例では、移動セマンティクスがキックインされることを期待していますが、残っているコピー操作が1つあります。最適化で下からの出力は次のようになります。タプル/タイの戻り値の最適化

Test duo output, non_reference tuple 
Default constructor invoked 
Parameter constructor invoked 
Copy constructor invoked 
Move Assignment operator invoked 
100 

関数内のタプルを作るのコピーコンストラクタの呼び出しは不要と思われます。これを取り除く方法はありますか?私はMSVC 2012コンパイラを使用しています。

#include <iostream> 
#include <tuple> 

class A 
{ 
public: 
    int value; 
    A() : value(-1) 
    { 
     std::cout << "Default constructor invoked" << std::endl; 
    } 

    explicit A(const int v) : value(v) 
    { 
     std::cout << "Parameter constructor invoked" << std::endl; 
    } 

    A(const A& rhs) 
    { 
     value = rhs.value; 
     std::cout << "Copy constructor invoked" << std::endl; 
    } 

    A(const A&& rhs) 
    { 
     value = rhs.value; 
     std::cout << "Move constructor invoked" << std::endl; 
    } 

    A& operator=(const A& rhs) 
    { 
     value = rhs.value; 
     std::cout << "Assignment operator invoked" << std::endl; 
     return *this; 
    } 

    A& operator=(const A&& rhs) 
    { 
     value = rhs.value; 
     std::cout << "Move Assignment operator invoked" << std::endl; 
     return *this; 
    } 
}; 

std::tuple<A, int> return_two_non_reference_tuple() 
{ 
    A tmp(100); 

    return std::make_tuple(tmp, 99); 
} 

int main(int argc, char* argv[]) 
{ 

     std::cout << "Test duo output, non_reference tuple" << std::endl;  
     A t3; 
     int v1; 
     std::tie(t3, v1) = return_two_non_reference_tuple(); 
     std::cout << t3.value << std::endl << std::endl; 

     system("pause"); 
     return 0; 
} 

答えて

6

を移動することですこの場合、tmpは左辺値です。あなたは右辺値参照にキャストするstd::moveを使用することができます。

return std::make_tuple(std::move(tmp), 99); 

これは、移動コンストラクタを使用するようにコンパイラに指示します。

1

あなたはtmpを移動されていません。

A tmp(100); 

return std::make_tuple(std::move(tmp), 99); 
3

コピーはreturn_two_non_reference_tuple()で行われます。それを削除する

一つの方法は、あなたがで

std::make_tuple(tmp, 99); 

を呼び出しているので、移動コンストラクタが自動的に呼び出されることはありませんtmp

std::tuple<A, int> return_two_non_reference_tuple() 
{ 
    A tmp(100); 
    return std::make_tuple(std::move(tmp), 99); 
} 

または別の方法

std::tuple<A, int> return_two_non_reference_tuple() 
{ 
    return std::make_tuple(A(100), 99); 
} 
3

コピーはここに起こる:

std::make_tuple(tmp, 99); 

あなたはtmp直接タプルに構築することができる可能性がありますことを見ることができますが、tmpからタプルへのコピーが省略されることはありません。本当に欲しいのは、std::tupleの内部のAintオブジェクトの構築に使用する引数を渡す方法です。 std::tupleにはこのようなことはありませんが、同じ効果を達成する方法があります。

2種類しかないので、std::pairを使用できます。これにはstd::piecewise_constructコンストラクターがあり、内部オブジェクトのコンストラクターに渡す引数を含む2つのstd::tuplesが必要です。

std::pair<A, int> return_two_non_reference_tuple() 
{ 
    return {std::piecewise_construct, 
      std::make_tuple(100), std::make_tuple(99)}; 
} 

このソリューションについての涼しい事はstd::tuplestd::pairから代入演算子を持っているので、あなたはまだ、呼び出しサイトでstd::tieを使用することができるということです。

std::tie(t3, v1) = return_two_non_reference_tuple(); 

出力からわかるように、コピーは消えています。これは、他の回答にあるような動きで置き換えられることはありません、それが完全に削除されます:

テストデュオ出力、デフォルトのコンストラクタは、パラメータのコンストラクタは

移動の割り当てを呼び出さ

呼び出さ

non_referenceタプルオペレータが呼び出す

Live Demo

+0

単純なケースの良い選択肢ですが、もっと多くのアクションがtmpで実行され、出力が増えればうまく動作しません。そして、私はそれがMSVC 2012で動作するとは思わない。 – thorsan

+0

コピーelionはここで起こっている。問題は、 'tmp'が' tuple'に移動していないことです。コピーコンストラクタはコピーされるタプルではなく、タプルに 'tmp'をコピーすることです。 – Simple

+0

@シンプル私はそれを知っている、多分私の答えは十分ではない。 – TartanLlama

関連する問題