std::moveとstd::forward

3749 ワード

std::move
C++11はmoveという名前のライブラリ関数を導入し,左値にバインドされた右値参照をヘッダファイルに定義できる.
この関数の定義は次のとおりです.
template 
typename remove_reference::type&& move(T&& t)
{
    return static_cast::type&&>(t);
}

コードからmoveはテンプレート関数であり,テンプレート関数のテンプレートパラメータはコンパイラによって関数実パラメータに基づいて導出されることが分かるので,次に異なるタイプの関数パラメータからテンプレート関数のインスタンス化結果を推定する.
1、実パラメトリックが左
int i = 0;
std::move(i);

moveに渡される実パラメータは左の値であることから,Tのタイプをint&と推定し,テンプレート関数を
remove_reference::type&& move(int& && t)
{
    return static_cast::type&&>(t);
}

remove_reference::typeの結果は非参照、すなわちintである.int&&&&は参照により折り畳まれてint&となる.結果は
int&& move(int& t)
{
    return static_cast(t);
}

static_castは変換関数を表示し、パラメータtをint&タイプからint&&タイプに変換し、最終的にint&&タイプに戻る.
2、実パラメータは右
int &&j = 0;
std::move(j);

moveに渡される実パラメータは右の値であることから,Tのタイプはintと推定され,テンプレート関数は
remove_reference::type&& move(int&& t)
{
    return static_cast::type&&>(t);
}

remove_reference::typeの結果はintであり、tはint&&タイプであるためstatic_cast(t)は依然としてint&&&タイプであるため、最終関数はint&&タイプを返す.
上の解析から,move関数は右値参照を返すことが分かった.
注意してください.move関数を使用する以上、受信オブジェクトはすでに失効していると考えられます.これにより、有効化または破棄のために値を再割り当てする以外に、操作するべきではありません.
std::forward
std::forwardコードは次のとおりです.
template inline
constexpr _Ty&& forward(typename remove_reference<_ty>::type& _Arg) _NOEXCEPT
{	
	// forward an lvalue as either an lvalue or an rvalue
	return (static_cast<_ty>(_Arg));
}

template inline
constexpr _Ty&& forward(typename remove_reference<_ty>::type&& _Arg) _NOEXCEPT
{	
	// forward an rvalue as an rvalue
	static_assert(!is_lvalue_reference<_ty>::value, "bad forward call");
	return (static_cast<_ty>(_Arg));
}
テンプレート関数の役割は、以下の例を参照して、完全な転送を実現することである.
template  void testFor(Type &&arg)
{
    innerFunc(std::forward(arg));
}
argがint&のような左の値の参照である場合、Typeはint&、argはstd::forwardの最初のテンプレートに適用され、_Tyはint&であり,それによってint&&&&&&を返し,int&に折り畳む.
argがint&&のような右の値の参照である場合、Typeはintとして導出され、argは右の値の参照であり、std::forwardの2番目のテンプレートに適用される.Tyがintであるため,戻り値はint&&&である.
すなわちstd::forwardは、パラメータの左値参照と右値参照の特性を保持します.
このtype traitsを使用しないと、転送中に問題が発生します.次のようになります.
void innerFunc1(int &a)
{
	cout << a << endl;
}

void innerFunc2(int &&a)
{
	cout << a << endl;
}

template  void testFunc(F f, Type &&arg1)
{
	f(arg1);
}

このテンプレートの目的は、F関数に対してtestFunc 2に入力された右値参照パラメータを呼び出すことであることが分かるが、testFunc(innerFunc 2,10)を呼び出すと、関数パラメータが左値式であるため、F(arg 1)はコンパイルに失敗する.
この問題を解決するには、testFunc関数の内容を次のように変更して解決することができます.
f(static_cast::type&&>(arg1));

これにより,右値参照に対してはうまく解決できるようであるが,Fが左値参照パラメータ関数を含む場合には適用されない.
std::forwardを使用すると、完璧な転送が可能です.
template  void testFunc(F f, Type &&arg1)
{
    f(std::forward(arg1));
}

これにより、fが左パラメータ関数であっても右パラメータ関数であっても、完全な転送を実現することができる.