右值引用可以从字面意思上理解,指的是以引用传递(而非值传递)的方式使用 C++ 右值
在 C++ 或者 C 语言中,一个表达式(可以是字面量、变量、对象、函数的返回值等)根据其使用场景不同,分为左值表达式和右值表达式。确切的说 C++ 中左值和右值的概念是从 C 语言继承过来的
值得一提的是,左值的英文简写为 “lvalue”,右值的英文简写为 “rvalue”。很多人认为它们分别是 "left value"、"right value" 的缩写,其实不然。
lvalue 是 “loactor value” 的缩写,可意为存储在内存中、有明确存储地址(可寻址)的数据,rvalue 译为 "read value",指的是那些可以提供数据值的数据(不一定可以寻址,例如存储于寄存器中的数据)。
通常情况下,判断某个表达式是左值还是右值,最常用的有以下 2 种方法。
1) 可位于赋值号(=)左侧的表达式就是左值;反之,只能位于赋值号右侧的表达式就是右值。举个例子:
int a = 5;
5 = a; // 错误,5 不能为左值
其中,变量 a 就是一个左值,而字面量 5 就是一个右值。值得一提的是,C++ 中的左值也可以当做右值使用,例如:
int b = 10; //b 是一个左值
a = b; //a、b 都是左值,只不过将 b 可以当做右值使用
2) 有名称的、可以获取到存储地址的表达式即为左值;反之则是右值。
以上面定义的变量 a、b 为例,a 和 b 是变量名,且通过 &a 和 &b 可以获得他们的存储地址,因此 a 和 b 都是左值;反之,字面量 5、10,它们既没有名称,也无法获取其存储地址(字面量通常存储在寄存器中,或者和代码存储在一起),因此 5、10 都是右值。
C++ 右值引用
前面提到,其实 C++98/03 标准中就有引用,使用 "&" 表示。但此种引用方式有一个缺陷,即正常情况下只能操作 C++ 中的左值,无法对右值添加引用。举个例子:
int num = 10;
const int &b = num;
const int &c = 10;
我们知道,右值往往是没有名称的,因此要使用它只能借助引用的方式。这就产生一个问题,实际开发中我们可能需要对右值进行修改(实现移动语义时就需要),显然左值引用的方式是行不通的
为此,C++11 标准新引入了另一种引用方式,称为右值引用,用 "&&" 表示
话说,C++ 标准委员会在选定右值引用符号时,既希望能选用现有 C++ 内部已有的符号,还不能与 C++ 98 /03 标准产生冲突,最终选定了 2 个 '&' 表示右值引用。
需要注意的,和声明左值引用一样,右值引用也必须立即进行初始化操作,且只能使用右值进行初始化,比如:
int num = 10;
//int && a = num; // 右值引用不能初始化为左值
int && a = 10;
和常量左值引用不同的是,右值引用还可以对右值进行修改。例如:
int && a = 10;
a = 100;
cout << a << endl;
程序输出结果为 100
另外值得一提的是,C++ 语法上是支持定义常量右值引用的,例如:
const int&& a = 10;// 编译器不会报错
但这种定义出来的右值引用并无实际用处。一方面,右值引用主要用于移动语义和完美转发,其中前者需要有修改右值的权限;其次,常量右值引用的作用就是引用一个不可修改的右值,这项工作完全可以交给常量左值引用完成。
学到这里,一些读者可能无法记清楚左值引用和右值引用各自可以引用左值还是右值,这里给大家一张表格,方便大家记忆:
C++ 左值引用和右值引用
表中,Y 表示支持,N 表示不支持