文 / 李博(光宇广贞)
《C++ 0x 之左值与右值》文中提到 std::forward() 和 std::move()。本文开头对之补充一句:
在操作函数返回值或函数参数时,匿名左值仍然为左值,左值可以具名;匿名右值仍然为右值,右值一旦具名成功,立即转变为左值。
举一个例子。使用 std::move() 方法向 Outer 传递右值后,使用 std::forward() 保证调用右值重载,而直接访问参数 t 的调用绑定到了左值重载。见下图:
因此,操作右值引用不能直接操作其变量名,否则将使右值引用具名,从而转为左值引用。操作右值引用必须使用 std::move()、std::forword() 等方法。将两个方法的实体展开如下:
这两个 identity 和 Remove_Reference 是干嘛用的?若不要它们,直接用 T&& 做为 forward 里 arg 的类型或 move 里返回类型呢?
不行。首先说 forward 方法。重新看如下代码:
template < typename T > void Outer ( T&& t )
{
Inner ( std::forward<T> ( t ) );
}
在《C++ 0x 之左值与右值》文中提到,我们使用 forward 的目的是保证参数的左右值性和只读性的准确传导。若不使用 identity 而只使用 T&& 的话,调用 std::forward<T>(t) 仍然没有问题;而当调用 std::forward(t),即不指明模板参数类型时,T&& 将由 t 推导,问题便来了。注意到 t 在传参数时,是点名调用,使 t 具名,故而无论原来 t 是左值还是右值,此刻都将视为左值,从而 T 被推导为左值引用,且 T&& 归化为左值引用,于是 forward 方法以左值引用类型接收参数 t,并以左值作为其返回类型。如是便违背了 forward 的本意。
而使用 identity 后,type 被指定为 T。注意这里,“::”算符就像一面墙,挡住了类型推导看见其左侧。因此,编译器不会认为 arg 参数是需要类型推导的,是被 identity::type 指定类型的(当然也强制使用 forward 时要注明模板参数,否则将无从推导)。这就保证了 forward 总是以右值引用类型接收参数 t。前文已经提到右值引用类型参数可以保留实参的一切信息。如是保证了 forward 的本意的实现。
最后,move 方法里面的 Remove_Reference 就好解释了。模板将根据 arg 推导类型,若 arg 是 const Type& 型,则 T 推导为 const Type& 型,而后代入特化模板,使 type 为 const Type,从而返回类型为 const Type&& 型。若 arg 是右值类型,则 type 也将归化为右值引用型。总之,Remove_Reference 模板类保证返回类型为右值引用。
所属分类:C++
参考:
C++ 0x 之 Lambda:贤妻与娇娃,你娶谁当老婆?听 FP 如何点化 C++