本文全都是翻译于 引用【1】
C++11中重要的新特性就是 右值引用,很多人都认为,类型声明中带有 “&&” 就代表着右值引用,这是不对的。
Widget&& var1 = someWidget; // “&&” 代表右值引用
auto&& var2 = var1; // here, “&&” 不是右值引用
template<typename T>
void f(std::vector<T>&& param); // “&&” 代表右值引用
template<typename T>
void f(T&& param); // “&&” 不是右值引用
上面这几个例子,就说明,并不是类型声明中带有 “&&” 的,就是右值引用。
类型声明中,带有 “&&”,可能有两种意图,需要程序员自己去自己区别。
- 右值引用;
- 万能引用,既可以是左值引用,也有可能是右值引用;
If a variable or parameter is declared to have type T&& for some deduced type T, that variable or parameter is a universal reference.(如果一个变量或参数被声明为 T&&,并且需要 推导 T 的类型的时候,此时该变量或参数即为万能引用。换个说法就是,有类型推导,&&为万能引用;没有类型推导,&&即为右值引用)
在实际应用中,大多数万能引用,多作为模板函数的参数。
万能引用和普通引用一样,一定要初始化。万能引用变量的最终类型是什么类型呢,这里涉及 &的折叠,之后再细讲。
- 如果用一个左值初始化 万能引用变量,则万能引用变量的类型为 左值引用
- 如果用一个右值初始化 万能引用变量,则万能引用变量的类型为 右值引用
因此,万能引用可以保持参数的类型(左值引用和右值引用)
如何区分左值和右值呢?
- 如果,能够获取到地址的变量,都是左值;
- 如果,一个变量的类型是左值引用,那么该变量一定是左值;
- 否则,变量为右值;
练习题1:
Widget&& var1 = someWidget;
auto&& var2 = var1;
var1 可以获取到地址,所以var1 是一个左值,他的类型是右值引用。
var2 是万能引用,因为var2 被一个左值,初始化,所以,var2的类型是左值引用,即Widget& var2 = var1;。因此var2 为左值,类型为 左值引用。
练习题2:
template<typename T>
void f(T&& param);
f(10); 因为参数10 是右值,所以 函数模板被实例化为右值int&&,即右值引用;
int x = 10; f(x);,因为参数 x 是左值,所以 T&& 被推导为int&,即左值引用;
练习题3:
template<typename T>
void f(T&& param); // && ≡ universal reference
template<typename T>
class Widget {
...
Widget(Widget&& rhs); // && ≡ rvalue reference
};
template<typename T1>
class Gadget {
...
template<typename T2>
Gadget(T2&& rhs); // T2和T1无关,T1确定下来后,根据传入的参数推导T2类型。&& ≡ universal reference
};
void f(Widget **&&** param); // && ≡ rvalue reference
练习题4:
template <class T, class Allocator = allocator<T> >
class vector {
public:
...
void push_back(T&& x); // 参数中T,是类类型,类类型确定下来,参数类型也就确定下来,无需推导。所以,&& ≡ rvalue reference
...
template <class... Args>
void emplace_back(Args&&... args); // && ≡ universal references
};