万能引用和引用折叠
引用折叠
引用折叠用于处理当类型别名或模板实例化导致“引用的引用”出现时的情况。
一个模板函数,根据定义的形参和传入的实参的类型,我们可以有下面四中组合:
- 函数定义的形参类型是左值引用,传入的实参是左值引用(&-&)
- 函数定义的形参类型是左值引用,传入的实参是右值引用(&-&&)
- 函数定义的形参类型是右值引用,传入的实参是左值引用(&&-&)
- 函数定义的形参类型是右值引用,传入的实参是右值引用(&&-&&)
template<typename T>
void foo(T& param) {
}
int main() {
    int x = 10;
    foo(x);  // param推导为:T& &
    foo(10); // param推导为:T& &&
}
/************************************/
template<typename T>
void foo(T&& param) {
}
int main() {
    int x = 10;
    foo(x);  // param推导为:T&& &
    foo(10); // param推导为:T&& &&
}
C++语法不允许直接声明“引用的引用”,但模板推导可能会间接产生这种形式。为了解决这个问题,C++定义了四条引用折叠规则:
| 组合 | 规则 | 结果 | 
|---|---|---|
| &-& | int& & | int&(左值引用) | 
| &-&& | int& && | int&(左值引用) | 
| &&-& | int&& & | int&(左值引用) | 
| &&-&& | int&& && | int&&(右值引用) | 
简记:
- 只要表达式中出现左值引用(&),最终结果就是左值引用(&)。
- 只有当表达式中全部都是右值引用(&&)时,最终结果才是右值引用(&&)。
万能引用
万能引用(Universal Reference)是一种特殊的引用类型,既可以是左值引用,用来绑定左值;也可以是右值引用,用来绑定右值。
它只出现在两种情况下:
- 模板函数的参数:template<typename T> void func(T&& param);
- auto推导的变量:- auto&& var = expr;
T&&只有在以下两个条件都满足的情况下才是万能引用:
- 必须是函数模板或auto变量的参数。
- T必须是未被任何修饰的(const、volatile等)模板参数,其类型完全由传入的参数推导而来。(const、volatile等修饰会导致退化为普通的右值引用)
区分右值引用和万能引用:万能引用必须涉及模板类型推导(auto本质上也是一种隐式的模板类型推导)
template<typename T>
void foo(T&& param)
{
    // param 是万能引用
}
int main
{
    int x = 10;
    // 传入左值,T被推导为int&
    foo(x);
    // 传入右值,T被推导为int&&。
    foo(20);
}