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