std::forward
分からんと言われたので解説的なエントリ。
まずC++には、テンプレート引数をrvalue referenceとして引数に設定しlvalueを渡すと、lvalue referenceになるという仕様がある*1。
#include<experimental/type_traits> #include<iostream> using namespace std::experimental; struct X{}; template<class _Tp> void f(_Tp&& x) { std::cout<<std::boolalpha<<is_rvalue_reference_v<decltype(x)><<"¥t"<< is_lvalue_reference_v<decltype(x)><<std::endl; } int main() { X x; f(x); // false true f(X()); // true false }
何故このような特殊な仕様があるかというと、rvalue referenceとlvalue referenceのどちらもを、一つの関数で済ませようという考えに準じているためである。
では、以上の前提知識を基に、std::forwardが必要となるシーンを示す。
#include<utility> struct X{}; template<class _Tp> void f(_Tp&& a) { X x(std::move(a)); } int main() { X x; f(x); // もうxは使えない。 }
関数f内でこのように記述してしまうと、ユーザーはムーブされている事に気付く事ができない。
そしてそもそも、ムーブするか否かはユーザー側が指定する事だ。ユーザーがムーブの記述をしたから、ムーブの動作がその関数内で発動するべきである。それはつまりどのように成し遂げるかというと、lvalueをユーザーが指定した場合はムーブを行わないのでlvalueのまま、rvalueをユーザーが指定した場合(std::moveを付与している場合)はrvalueに出来れば、願いは叶う事となる。その願いを叶えた標準ライブラリが、std::forwardである。そしてその願いが叶えられた時、人々はそれをPerfect forwardingと呼ぶのである。
#include<utility> struct X{}; template<class _Tp> void f(_Tp&& a) { _Tp x(std::forward<_Tp>(a)); } int main() { X x; f(x); // lvalue referenceとなりムーブは行われずコピーされる f(std::move(x)); // ムーブ指定(rvalue)なのでムーブされる }
願いと言っても、forwardの本質はテンプレートメタプログラミングによる渡された型への規定チェックと仕様*1を利用したキャストである。
#include<experimental/type_traits> #include<iostream> #include<utility> using namespace std::experimental; struct X{}; template<class _Tp> void f(_Tp&& x) { std::cout<<std::boolalpha<<is_rvalue_reference_v<decltype(std::forward<_Tp>(x))><<"\t"<< is_lvalue_reference_v<decltype(std::forward<_Tp>(x))><<std::endl; } int main() { X x; f(x); // false true f(std::move(x)); // true false }
余談
Forwardとは関係なく、規格*1関連で。
gcc4.4.7とか、少しバージョンが低いコンパイラだと以下のようなコードが通るので注意が必要である。
int main() { struct X{}x; X&& r=x; // ill-formed X&& r1=X(); }
これは、現在のドラフト内容に準じていない。当エントリの冒頭でアンダーラインを引いているように、テンプレート引数をrvalue referenceとして引数に設定しlvalueを渡さなければ、lvalue referenceにはならないので、該当箇所はill-formedのはずである。
なぜこのような事が起きているかというと、この辺りのバージョンリリースの時期にドラフトの文言が変更されたからである。
*1:§ 14.9.2.1 Deducing template arguments from a function call p3