前面我们说过,形参为万能引用型别时,将根据实参的型别来决定最终param的型别:
template<class T>
void func(T&& param) {
// do something
}
Widget w;
func(w); // w为左值,T为Widget&,param为Widget&
func(Widget()); // 实参为右值,T为Widget,param为Widget&&
之所以会有上面结果的推导结果,就是因为引用折叠的存在,同样,std::forward中也深刻践行了引用折叠,引用折叠的规则如下:
如果任一引用为左值引用,则结果为左值引用,否则(即两个皆为右值引用)结果为右值引用。
引用折叠出现的语境有四种,分别为模板实例化、auto变量的型别生成、typedef和别名声明、decltype。
模板实例化和auto变量的型别生成技术细节上其实是一样的,因为auto型别推导和模板型别推导在本质上就是一模一样的。
模板实例化
template<typename T>
void func(T&& param); // 模板函数
Widget widgetFactory(); // 返回右值的工厂函数
Widget w; // 左值变量
// 实参为左值,T被推导为Widget&(左值引用),所以param的推导结果为Widget& &&,即Widget&
func(w);
// 实参为右值,T被推导为Widget(右值),所以param的推导结果为Widget&&,即Widget&&
func(widgetFactory());
auto变量的型别生成
Widget w; // 左值变量
// auto型别为Widget&,w1为Widget& &&,即Widget&
auto&& w1 = w;
// 以右值初始化w2,auto被推导为Widget,所以w2为Widget&&型别
auto&& w2 = widgetFactory();
生成和使用typedef和别名声明
假设我们有个类模板Widget,内嵌一个右值引用型别的typedef:
template <typename T>
class Widget{
public:
typedef T&& ReturnValue;
}
再假设我们使用左值引用型别来实例化该Widget:
Widget<int&> w;
在Widget中以int&代入T的位置,则得到如下的typedef:
typedef int& && ReturnValue;
引用折叠又将其简化为:
typedef int& ReturnValue;
decltype
如果在分析一个涉及decltype的型别过程中出现了引用的引用,则引用折叠会将其简化掉