C++11:右值引用与万能引用

347 阅读3分钟

本文全都是翻译于 引用【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);  // “&&” 不是右值引用

上面这几个例子,就说明,并不是类型声明中带有 “&&” 的,就是右值引用。

类型声明中,带有 “&&”,可能有两种意图,需要程序员自己去自己区别。

  1. 右值引用;
  2. 万能引用,既可以是左值引用,也有可能是右值引用;

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. 如果用一个左值初始化 万能引用变量,则万能引用变量的类型为 左值引用
  2. 如果用一个右值初始化 万能引用变量,则万能引用变量的类型为 右值引用

因此,万能引用可以保持参数的类型(左值引用和右值引用)

如何区分左值和右值呢?

  1. 如果,能够获取到地址的变量,都是左值;
  2. 如果,一个变量的类型是左值引用,那么该变量一定是左值;
  3. 否则,变量为右值;

练习题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
};

引用:

【1】Universal References in C++11 -- Scott Meyers