最新学习C++11,发现引用折叠和完美转发的概念非常的不好理解。
今天发现了一篇文章,可以把这两个概念讲的特别清楚。
from:https://zhuanlan.zhihu.com/p/50816420
1、引用折叠
T& & = T&
T& && = T&
T&& & = T&
T&& && = T&&
左侧这么多连续的&在C++中的语法是不允许的,但是是在使用模板泛化编程中是会遇到的, 这个时候,编译器会自动根据上面的规则把进行转换。
有网友总结的很好:
左值引用会传染,只要有一个&,结果一定是左值引用
2、模板中的类型推断
template <class T>
void myprint(T && t)
{
std::cout << "myprint(T &&)" << std::endl;
}
这个模板函数,可以根据输入,推断出T的类型。
这里面有一个情况必须要清楚,就是这个函数的参数的最终类型,一定是个引用
1、如果T是int型, t的类型是 int &&
2、如果T是int&型,t的类型是 int& && = int&
3、如果T是int&&型,t的类型是 int && && = int&&
总结起来,t一定是一个左值引用或者右值引用类型。
int a = 10;
myprint(a);
myprint(20);
当按照这个方式调用时,a是一个左值引用,根据前面第2条,可以推断出,T的类型是int&。
20是一个右值,根据第1条规则,编译器推断,T的类型为int。
即如果实参是左值,T被推断为实参类型的引用类型。
如果实参是右值,T被推断为实参的类型。
3、完美转发
在模板函数中会遇到的一个问题是:无论模板函数的实参是左值还是优质,在模板函数内部都有一个局部变量与之对应,此时使用这个参数调用其他函数时,它都是一个左值。 所以在这个时候,需要在模板函数内部,有一个机制,保证接收到的参数保持他们实参原来的值类型。
完美转发中,用到了一个很核心的函数std::forward。如下是它的定义
template<typename T>
T&& forward(T ¶m)
{
return static_cast<T&&>(param);
}
从定义上可以看到无论T是哪种类型,根据引用折叠的规则,参数param都是一个左值引用类型。
从前面的分析我们知道
如果实参是左值,T被推断为实参类型的引用类型。
如果实参是右值,T被推断为实参的类型
当模板函数的实参是a(左值)时,T被推断为int &。根据折叠规则,带入上面的模板函数,生成的函数为:
int & forward(int ¶m)
{
return static_cast<int&>(param);
}
返回的是一个左值引用。 和实参类型一致
当模板函数的实参是20(右值)时,T被推断为int 。根据折叠规则,带入上面的模板函数,生成的函数为:
int && forward(int ¶m)
{
return static_cast<int&&>(param);
}
返回的是一个右值引用。和实参类型一致