概述
C++11 在 C++98/03 标准的基础上,对 C++ 语言增添了约 140个新特性。本节要讲的右值引用就是众多新特性中的一个。右值引用是一种新的 C++ 语法,基于右值引用引申出的 2 种 C++ 编程技巧,分别为移动语义和完美转发。
C++左值和右值
左值:lvalue,loactor value;存储在内存中、有明确存储地址(可寻址)的数据。
右值:rvalue,read value;可以提供数据值的数据(不一定可以寻址,例如存储于寄存器中的数据)
一个表达式(可以是字面量、变量、对象、函数的返回值等)根据其使用场景不同,分为左值表达式和右值表达式。
判断依据(通常):
-
可以位于赋值号(=)左侧的表达式就是左值;反之,只能位于赋值号右侧的表达式就是右值。
举个例子:
int a = 5; 5 = a; //错误, 字面量 5 就是一个右值 int b = 10; // b 是一个左值 a = b; // a、b 都是左值,左值 b 也可以当做右值使用
-
有名称的、可以获取到存储地址的表达式即为左值;反之则是右值。
以上面定义的变量 a、b 为例,a 和 b 是变量名,且通过 &a 和 &b 可以获得他们的存储地址,因此 a 和 b 都是左值;
反之,字面量 5、10,它们既没有名称,也无法获取其存储地址(字面量通常存储在寄存器中,或者和代码存储在一起),因此 5、10 都是右值。
注意,以上 2 种判定方法只适用于大部分场景。由于本节主要讲解右值引用,因此这里适可而止,不再对 C++ 左值和右值做深度剖析,感兴趣的读者可自行研究。
C++右值引用
回顾左值引用
C++98/03 标准中加入了左值引用 “&” ,即可以为一个左值建立引用。
int num = 10;
int &b = num; //正确,引用b可以绑定左值
int &c = 10; //错误,引用b不可以绑定右值
同时,C++98/03 标准允许使用常左值引用绑定右值,例如:
int num = 10;
const int &b = num; //常左值引用,绑定左值
const int &c = 10; //常左值引用,绑定右值
右值引用的必要性
右值往往是没有名称的,因此要使用它只能借助引用的方式。
实际开发中我们可能需要对右值进行修改(实现移动语义时就需要),显然常左值引用的方式不满足需求。
为此,C++11 标准引入了另一种引用方式:右值引用,用 “&&” 表示。
右值引用符号选用现有C++内部已有的符号,且没有与 C++ 98 /03 标准左值引用& 产生冲突
定义和使用
普通右值引用
右值引用也必须立即进行初始化操作,且只能使用右值进行初始化:
int num = 10;
//int &&a = num; //右值引用不能绑定左值
int && a = 10;
a = 100; //和常左值引用不同,右值引用可以对右值进行修改
cout << a << endl; //输出 100
常右值引用
const int&& a = 10;//编译器不会报错,但是无实际用途
左值引用和右值引用概览:
- | 非常量左值 | 常量左值 | 非常量右值 | 常量右值 | 使用场景 |
---|---|---|---|---|---|
非常量左值引用(普通引用) | Y | N | N | N | 一般引用的使用 |
常左值引用(常引用) | Y | Y | Y | Y | 常用于类中拷贝构造函数 |
非常量右值引用(普通右值引用) | N | N | Y | Y | 移动语义、完美转发 |
常量右值引用 | N | N | Y | Y | 无实际用途 |
注:
- Y表示支持,N表示不支持
- 左列:引用类型;上行:被绑定的值类型
引用类型 && ra = 被绑定的值a;
为什么常右值引用无实际用处? 右值引用主要用于移动语义和完美转发,其中前者需要有修改右值的权限;而常右值引用的作用是引用一个不可修改的右值,这项工作完全可以交给常左值引用完成。
将亡值
C++11 标准中对右值做了更细致的划分,分别称为
- 纯右值(Pure value,简称 pvalue): C++98/03 标准中的右值
- 将亡值(eXpiring value,简称 xvalue )。指和右值引用相关的表达式(比如某函数返回的 T && 类型的表达式)
对于纯右值和将亡值,都属于右值,有时间可以进一步研究。TODO