引用折叠

引用折叠用于处理当类型别名或模板实例化导致“引用的引用”出现时的情况。

一个模板函数,根据定义的形参和传入的实参的类型,我们可以有下面四中组合:

  1. 函数定义的形参类型是左值引用,传入的实参是左值引用(& - &
  2. 函数定义的形参类型是左值引用,传入的实参是右值引用(& - &&
  3. 函数定义的形参类型是右值引用,传入的实参是左值引用(&& - &
  4. 函数定义的形参类型是右值引用,传入的实参是右值引用(&& - &&
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);
}