暗黙のmoveとNRVO

VS2010(VC10)にて関数で返す値についての扱いをついったーで突っ込まれて実際書いて確かめた時のメモ。

#include <iostream>
#include <vector>
#include <boost/timer.hpp>

struct Test{
	std::vector<int> tmp;
	Test(){
		std::cout << "コンストラクタ" << std::endl;
	}
	~Test(){
		std::cout << "デストラクタ" << std::endl;
	}

	Test(const Test& obj) : tmp( obj.tmp ){
		std::cout << "コピーコンストラクタ" << std::endl;
	}
	
	Test(Test&& obj){
		std::swap(tmp,obj.tmp);
		std::cout << "ムーブコンストラクタ" << std::endl;
	}
};


Test get(int n, boost::timer& timer){
	Test tmp;
	for(int i=0; i < n; ++i){
		tmp.tmp.push_back(i*2+3*i);
	}
	std::cout << timer.elapsed() << std::endl;
	timer.restart();
	return tmp;
}


int main(){
	boost::timer t;
	auto tmp = get(10000000,t);
	std::cout << t.elapsed() << std::endl;
}

実行結果:

コンストラクタ
0.072
0
デストラクタ

NRVOが聞いてコピーもムーブも起こらずにそのまま置き換えられた。
これが最速だが、ifでreturn分けたりすると最適化が切れる場合が多い。
そうするとどうなるか。てっきり普通にコピーされるのかなーとか思っていた、VC2008脳だった。が。

#include <iostream>
#include <vector>
#include <boost/timer.hpp>

struct Test{
	std::vector<int> tmp;
	Test(){
		std::cout << "コンストラクタ" << std::endl;
	}
	~Test(){
		std::cout << "デストラクタ" << std::endl;
	}

	Test(const Test& obj) : tmp( obj.tmp ){
		std::cout << "コピーコンストラクタ" << std::endl;
	}
	
	Test(Test&& obj){
		std::swap(tmp,obj.tmp);
		std::cout << "ムーブコンストラクタ" << std::endl;
	}
};


Test get(int n, boost::timer& timer){
	Test tmp;
	for(int i=0; i < n; ++i){
		tmp.tmp.push_back(i*2+3*i);
	}
	std::cout << timer.elapsed() << std::endl;
	timer.restart();
	if( n%2 == 0 ){
		return tmp;
	}
	tmp.tmp.push_back(1192);
	return tmp;
}


int main(){
	boost::timer t;
	auto tmp = get(10000000,t);
	std::cout << t.elapsed() << std::endl;
}

実行結果:

コンストラクタ
0.072
ムーブコンストラクタ
デストラクタ
0.001
デストラクタ

あ……ハイ。そうですよね、C++0x(11?)では関数内で宣言された変数が返り値になった場合には原則moveされるんでしたねそういえばはい。


という事で暗黙moveされんじゃねーかstd::moveとか書く必要あるのすげー特定場面じゃないですかやだー!!
ということでしたというメモ。
------------------------------
コメント頂いて気づいたけども、「暗黙変換(継承先スマポ=>継承元スマポのような)」の場合は暗黙moveもされないし、もちろんNRVOもかからないので、std::moveを使うのはすげー特定場面でなく、単に特定場面くらいのニュアンスになりそう。
もちろん暗黙変換考える際にはポリモーフィックな感じに使う場面がメインと考えられて、moveされなくても誤差い場合が多いだろうけれども、そうでない場面も全然ありうるので忘れてはいけない場合でした。