如上代码中,发生了引用折叠,将TR展开,得到 T& & v = 1(注意这里不是右值引用)。 这里的 T& + & 被折叠为 T&。更为详细的,根据TR的类型定义,以及v的声明,发生的折叠规则如下:
再谈转发
那么上面的引用折叠规则,对完美转发有什么用呢?我们注意到,对于T&&类型,它和左值引用折叠为左值引用,和右值引用折叠为右值引用。基于这种特性,我们可以用 T&& 作为我们的转发函数模板参数:
当传入左值引用 X& 时:
在C++11中,static_cast<T&&>(t) 可以通过 std::forward<T>(t) 来替代,std::forward是C++11用于实现完美转发的一个函数,它和std::move一样,都通过static_cast来实现。我们的Forward函数最终变成了:
T& + & = T&
T& + && = T&
T&& + & = T&
T&& + && = T&&
上面的规则被简化为:只要出现左值引用,规则总是优先折叠为左值引用。仅当出现两个右值引用才会折叠为右值引用。再谈转发
那么上面的引用折叠规则,对完美转发有什么用呢?我们注意到,对于T&&类型,它和左值引用折叠为左值引用,和右值引用折叠为右值引用。基于这种特性,我们可以用 T&& 作为我们的转发函数模板参数:
template<typename T>
void Forward(T&& t)
{
Do(static_cast<T&&>(t));
}
这样,无论Forward接收到的是左值,右值,常量,非常量,t都能保持为其正确类型。当传入左值引用 X& 时:
void Forward(X& && t)
{
Do(static_cast<X& &&>(t));
}
折叠后:
void Forward(X& t)
{
Do(static_cast<X&>(t));
}
这里的static_cast看起来似乎是没有必要,而它实际上是为右值引用准备的:
void Forward(X&& && t)
{
Do(static_cast<X&& &&>(t));
}
折叠后:
void Forward(X&& t)
{
Do(static_cast<X&&>(t));
}
前面提到过,可以接收右值的右值引用本身却是个左值,因为它具名并且可以取值。因此在Forward(X&& t)中,参数t已经是一个左值了,此时我们需要将其转换为它本身传入的类型,即为右值。由于static_cast中引用折叠的存在,我们总能还原参数本来的类型。在C++11中,static_cast<T&&>(t) 可以通过 std::forward<T>(t) 来替代,std::forward是C++11用于实现完美转发的一个函数,它和std::move一样,都通过static_cast来实现。我们的Forward函数最终变成了:
template<typename T>
void Forward(T&& t)
{
Do(std::forward<T>(t));
}
可以通过如下代码来测试:
#include<iostream>
using namespace std;
void Do(int& i) { cout << "左值引用" << endl; }
void Do(int&& i) { cout << "右值引用" << endl; }
void Do(const int& i) { cout << "常量左值引用" << endl; }
void Do(const int&& i) { cout << "常量右值引用" << endl; }
template<typename T>
void PerfectForward(T&& t){ Do(forward<T>(t)); }
int main()
{
int a;
const int b;
PerfectForward(a); // 左值引用
PerfectForward(move(a)); // 右值引用
PerfectForward(b); // 常量左值引用
PerfectForward(move(b)); // 常量右值引用
return 0;
}










