T&& 有两种含义:
右值引用,只能绑定到右值上。万能引用(作者称为 “universal references” ),可以绑定到任何类型的变量上,包括左值或右值,const 或 non-const,volatile 或 non-volatile,以及以上的任何组合。万能引用出现在两个情景中:
模板函数的参数, 形式为:
template
auto 变量推导,形式为:
auto&& var2 = var1; // var2 is a universal reference
万能出现的必要条件是存在类型推导。如果看到了不存在类型推导的 T&&,那么就是右值引用,例如:
void f(Widget&& param); // no type deduction; // param is an rvalue referenceWidget&& var1 = Widget(); // no type deduction; // var1 is an rvalue reference
除了存在类型推导,万能引用还要求引用出现的形式必须为 T&&,T 不能是任何复合类型或带修饰的类型。以下例子也为右值引用而非万能引用:
template
即使在模板函数中看到了 T&& 参数,也不能立刻敲定其为万能引用,因为在模板中不保证会存在类型推导。例如对于 STL 中的 std::vector:
template
push_back() 的参数虽然是 T&& 的形式,但这里没有类型推导,因为 push_back() 必须依赖于一个已经实例化的 vector 而存在,vector 实例化时就已经确定了 T 的类型。调用时,参数 T&& 已经确定为某个具体类型(如 int&&),是右值引用而非万能引用。相反,vector 的另一个成员函数 emplace_back 就会进行类型推导,其形式如下:
template
注意这里的模板参数 Args 与 vector 的模板参数 T 无关。
auto 的情形显然符合以上条件:显然存在类型推导,并且形式正确(auto&&)。
本 Item 的所有内容都是 “谎言”(高情商:是一种抽象),其底层的真相被称为 引用折叠(reference collapsing),在 Item 28 进行讨论。不过这种抽象对于正确理解源码(我看到的 T&& 只能绑定到右值还是任何对象?)很有帮助。(也有助于理解后面 Item 25 和 Item 26 的内容)
总结 如果一个模板函数的参数的形式为 T&&,且类型 T 涉及推导,或者一个对象是由 auto&& 声明,那个参数或对象就是万能引用。如果类型声明的形式不恰好是 type&&,或者没有涉及类型推导,那么 type&& 就是一个右值引用。万能引用由右值对象初始化时,就是右值引用;由左值对象初始化时,就是左值引用。