认识右值引用之前,可以先回忆一下左值、右值、左值引用等概念。
左值(lvalue)这一术语来源于C语言,用来指代那些可以用在赋值表达式左侧的东西,例具名对象、在栈和堆上分配的对象或者其他对象的成员,总之就是有存储空间的东西。
int a; //变量 a 是左值
B b = new B(); //假设B 是一个自定义类,则 b 也是一个左子。
//若类B 有一个 int类型的成员变量c, 则B::c 也是一个左值。
术语右值(rvalue)也是源于C语言,指的是只能在赋值表达式右侧出现的东西,如字面值和临时对象。
int i = 42; //字面值42 是右值
string s = string("abc"); //匿名临时对象 string("abc") 是右值
归纳:当一个对象被用作返回右值的表达式的时候,用的是对象的值(内容);当对象被用作左值的时候,用的是对象的身份(内存)。
int i = 42; //变量i 是一个左值, 常量值42 是右值。
int j = i + 1; //i 被用作返回右值的表达式,使用的是i 的值42,即表达式i + 1 相当于42 + 1,返回的是右值。
i = 50; //i 被用作左值,用的是对象的身份,即i 指向的内存存储的是 50。
左值引用(lvalue reference)指的是必须绑定到左值的引用,通过 & 来获得左值引用。
左值引用的绑定特性:我们不能将左值引用绑定到要求转换的表达式、字面常量或是返回右值的表达式。
int i = 42; //左值i, 右值42
int& r = i; //左值引用r, 绑定到左值i 上,即r 引用 i
int& a = 42; //错误。42是右值(字面常量),左值引用不能绑定到右值上
const int & a = 42; //正确。可以将一个const 的引用绑定到一个右值上
double b = 3.14; //声明一个double类型变量,初始值为3.14
int& c = b; //错误。左值引用不能绑定到要求转换的表达式
int& d = i + 1; //错误。表达式i + 1 返回右值,而左值引用不能绑定到返回右值的表达式。i*2,i/2等同理
为了支持移动操作,新标准引入了一种新的引用类型-右值引用(rvalue reference)。
所谓右值引用就是必须绑定到右值的引用。通过 && 而不是 & 来获得右值引用。
右值引用有一个重要的性质-只能绑定到一个将要销毁的对象。
即:①所引用的对象将要被销毁 ②该对象没有其他用户
右值引用有着和左值引用完全相反的绑定特性。
int&& i = 42; //正确。绑定到右值的右值引用i
int j = 42;
int&& k = j; //错误。j 是一个左值
我们一般的认知是变量是左值,但是变量也可以看作是只有一个运算对象而没有运算符的表达式。
《c++ primer》中对表达式的定义:表达式由一个或多个运算对象组成,对表达式求值将得到一个结果。字面值和变量是最简单的表达式,其结果就是字面值和变量的值。
类似其他任何表达式,变量表达式也有左值/右值属性。变量表达式都是左值,带来的结果就是:我们不能将一个右子引用绑定到一个右值引用类型的变量上。
int&& rr1 = 42; //右值引用rr1
int&& rr2 = rr1; //错误,表达式(这里不以变量称呼rr1)是左值
因为右值是临时对象,而变量是持久的,直至离开作用域时才会被销毁,所以变量是左值。因此我们不能将一个右值引用绑定到一个变量上(左值),即使这个变量是右值引用类型也不行!