完美转发指的是函数模板可以将自己的参数“完美”地转发给内部调用的其它函数。所谓完美,就是能保证被转发参数的左、右值属性不变。
引入
template<typename T>
void function(T t) {
otherdef(t);
}
如上所示,function() 函数模板中调用了 otherdef() 函数。完美转发指的是:如果 function() 函数接收到的参数 t 为左值,那么该函数传递给 otherdef() 的参数 t 也是左值;反之如果 function() 函数接收到的参数 t 为右值,那么传递给 otherdef() 函数的参数 t 也必须为右值。
很明显,function并未实现完美转发,无论传递给function的是左值还是右值,最终传递给otherdef的都是左值。
事实上,c++11之前也可以通过重载函数模板来实现完美转
void otherdef(int& t)
{
cout << "左值" << endl;
}
void otherdef(const int& t)
{
cout << "右值" << endl;
}
//重载函数模板,分别接收左值和右值
//接收右值参数
template<typename T>
void function(const T& t)
{
otherdef(t);
}
//接收左值参数
template<typename T>
void function(T& t)
{
otherdef(t);
}
int main()
{
function(5);
int x = 1;
function(x);
return 0;
}
完美转发
显然,使用重载的模板函数实现完美转发也是有弊端的,此实现方式仅适用于模板函数仅有少量参数的情况,否则就需要编写大量的重载函数模板,造成代码的冗余。为了方便用户更快速地实现完美转发,C++ 11 标准中允许在函数模板中使用右值引用来实现完美转发。
C++11 标准中规定,通常情况下右值引用形式的参数只能接收右值,不能接收左值。但对于函数模板中使用右值引用语法定义的参数来说,它不再遵守这一规定,既可以接收右值,也可以接收左值(此时的右值引用又被称为“万能引用”)。
仍以 function() 函数为例,在 C++11 标准中实现完美转发,只需要编写如下一个模板函数即可:
template<typename T>
void function(T&& t)
{
otherdef(t);
}
此模板函数的参数 t 既可以接收左值,也可以接收右值。但仅仅使用右值引用作为函数模板的参数是远远不够的,还有一个问题继续解决,即如果调用 function() 函数时为其传递一个左值引用或者右值引用的实参,如下所示:
int n = 1;
int& num = n;
function(num);
int&& num2 = 11;
function(num2);
相应的,T被初始化int&和int&&,而function则变为function(T&&&)和function(T&&&&),c++特意为这种情况指定了新的匹配规则:
funciton(T&&&)========function(T&)
function(T&&&&)=======function(T&&)
还存在一个问题,即无论传入的形参是左值还是右值,对于函数模板内部来说,形参既有名称又能寻址,因此它都是左值。那么如何才能将函数模板接收到的形参连同其左、右值属性,一起传递给被调用的函数呢?
C++11 标准的开发者已经帮我们想好的解决方案,该新标准还引入了一个模板函数 forword(),我们只需要调用该函数,就可以很方便地解决此问题。仍以 function 模板函数为例,如下演示了该函数模板的用法:
void otherdef(int& t)
{
cout << "左值" << endl;
}
void otherdef(const int& t)
{
cout << "右值" << endl;
}
template<typename T>
void function(T&& t)
{
otherdef(forward<T>(t));
}
int main()
{
function(5);
int i = 1;
function(i);
return 0;
}