左值和右值
就变量而言,对于一些变量,我们只会读取并使用它们的值,而不会改变他们的值(只读);对于其余的变量,我们既会读取它们的值,有的时候还会改变它们的值(读写),这是很常见的。在C++中,前一种变量称为右值,后一种变量称为左值,例如:
int a = 1; // a是左值,1是右值
C++中的左值和右值
稍稍不同的一点是,在C++中,一个变量是左值还是右值并不取决于我们如何使用它,这仅仅由它的类型和生成的方式决定,这是由C++标准规定的。(下文中提到的左值变量和右值变量均指的是C++标准中所规定的 “左值” 和 “右值”)
在C++中,我们可以读写左值变量,但我们不能(而不仅仅是 “不会” )改变右值的值(仅有的两个例外将在下文中提到)。不能对右值进行取址(这点不存在例外)。
C++中的左值表达式和右值表达式
在C++中,如果一个表达式表示了一个左值变量,那么把这个表达式称为 “左值表达式”;如果一个表达式表示了一个右值变量,那么把这个表达式称为 “右值表达式”。
例如,对于具有返回值的函数调用表达式,如果函数返回引用类型,那么函数调用表达式指示着返回值 “引用” 的变量,如果这个变量是左值(右值),那么函数调用表达式是左值(右值)表达式;如果函数返回其他类型,那么函数调用表达式指示着一个匿名的临时变量,这个匿名变量用于存放函数的返回值,这个匿名变量是一个右值,因此函数调用表达式是右值表达式。
C++中的常量左值、右值引用
尽管C++标准中的规定符合大部分实际情况,例如,数字类型的字面值(1、2、3、3.56、7 …)被规定为右值,而的确没有哪一个人会想要改变数字1的值,但仍然存在一些疏漏。
如果你细心观察,不难发现我们有时候会想要改变一些右值的值,而有的时候又希望一些左值具有不可改变的值。为了应付这种情况,C++标准中引入了两个例外。对于前者,我们可以通过右值引用来改变右值的值,对于后者,我们可以使用常量左值。
常量左值在声明时带有 const 限定符,这种变量必须初始化且初始化后变量值不能再改变,请看代码:
const int a = 10; // 声明了常量左值a,初始化为10
a = 15; // 非法,试图通过赋值改变常量左值a的值
右值引用是对右值的 “引用”,正如通过左值引用
可以改变左值变量的值一样,通过右值引用也可以改变右值变量的值。
- 左值引用通过在变量名前加 & 声明,右值引用通过在变量名前加 && 声明。
- 一条语句内允许定义多个引用,但每一个引用的名字前都要加上 & 或 &&。
- 引用必须初始化。(请参考我的另一篇文章:引用的初始化)
- 引用不是对象,因此不存在引用的引用,也不存在引用的指针。
请看代码:
#include <iostream>
class A
{
public:
int value = 10;
};
A test();
int main()
{
A instance; // 类A的一个实例变量
A &&a = test() , &b = instance; /* 定义一个A类型的右值引用a
绑定到test()的返回值,定
义一个A类型的左值引用b绑
定到实例变量instance*/
a.value = 15;
std::cout << a.value << std::endl << b.value << std::endl;
return 0;
}
A test()
{
A a;
return a;
}
运行结果:
15
10