C++11 std::forward完美转发和std::move移动语义

std::forward完美转发其实什么也没有转发;std::move移动语义,其实什么也没有移动。

如是说...

std::forward完美转发

在模板函数的嵌套调用中,可能用“模板函数A”的参数传给“模板函数B”,这时候由于类型推导的原因。就可能会导致“模板函数B”得到的参数损失了某些特性。举个例子:

void f(int&& t);
void f(const int& t);

template <typename T>
void flip(T&& t) {
f(t);//f(std::forward<T>(t));
}

对于以上flip函数,不管调用flip(i);还是flip(std::move(i));都只会调用void f(const int& t);,从不会去调用右值的重载。所以,需要一个完美转发来避免参数损失某些特性。比如,调用flip(std::move(i));时,明显是传入了一个右值。 此时,T被推导为int&&,整个函数就成了如下:

void flip(int&& &&t){//C++只能间接使用int&&&&,无法直接使用
f(t);
}

右值引用的右值引用,真奇怪。但是,C++标准中有以下“折叠规则”,允许这种奇怪的现象。 1. X& &X& &&X&& &都将折叠为X&; 2. 只有X&& &&会被折叠为X&&

所以,std::forward<T>(i);只会在,T被推导为X&&时,才进行转发,否则,保持不变就可以了。还有一种情况,std::forword<T>(i);可以去掉const,举个例子:

template <typename T>
void flip(const T& t){
f(t);//f(std::forward<T>(t));
}

如果调用flip(i);时传入的是一个非consti,但是在flip->f(i);时,传给f(i)函数的总是const类型了...必须使用f(std::forward<T>(t));还原,原来的左值特性。

std::move移动语义

std::move其实什么也没移动,只是无条件做了一个强制类型转换。它的实现如下:

template <typename T>
typename remove_reference<T>::type&& move(T&& t) {
return static_cast<typename remove_reference<T>::type&&>(t);
}

remove_reference<T>是类型traits其中的一个模板,表示“移除引用”。是的,std::move不应该返回一个引用。std::move语法,需要类实现的时候进行支持。也就是需要有接受右值的拷贝函数。正是因为这样,一旦执行obj2 = std::move(obj1);语句之后,obj内容就是不确定的。


References: [1] Stanley B. Lippman《C++ Primer(第5版)》 [2] 祁宇《深入C++11代码优化与工程级应用》 [3] Scott Meyers《Effective Modern C++》