如何把一个简单的事情搞复杂。

简单的开始

左值和右值是 C 和 C++ 语言中表达式值的一种分类。每个表达式都有类型和值类别。

1 + 1 的类型为 int ,值类别为右值。

在 C++ 03 左值和右值很容易区分。

左值(left value, lvalue)为内存地址,可以出现在赋值等号的左边和右边。

1
2
3
int x;
x = 1; // x 是左值
int y = x; // x 可以出现在赋值等号的右边

右值(right value, rvalue)为只能出现在赋值等号右边的表达式。

1
2
3
int x;
x = 1; // x 是右值
1 = x; // 出错, 1 是右值,只能出现在赋值等号的右边。

复杂的延续

C++ 11 引入了移动语义。

对于类型 T ,T&& 称为右值引用 (rvalue reference)。T& 称为左值引用(lvalue reference)。

如果类型 T 实现了移动构造函数,则称 T 为可移动的。

引入了移动语义,问题随之而来。

1
int&& f() {...}

那么 f() 是左值还是右值?

f() 不是左值因为左值不可移动。

f() 不是右值因为可以出现在赋值等号的左边。

所以引入一种新的值类别,称为亡值(eXpiring value, xvalue)。f() 为亡值。

原先的左值变成了泛左值(generalized value, glvalue)。

原先的右值变成了纯右值(pure value, prvalue)。

定义新的左值为非亡值的泛左值。

定义新的右值为纯右值或者亡值。

用图表示就是

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

+----------------+
| |
+--------->+ 左 值 |
| | |
+----------------+ | +----------------+
| | |
+------>+ 泛 左 值 +-------+
| | | |
+----------------+ | +----------------+ | +----------------+
| | | | | |
| 值 类 别 +-------+ +--------->+ 亡 值 |
| | | | | |
+----------------+ | +----------------+ | +----------------+
| | | |
+------>+ 纯 右 值 +-------+
| | |
+----------------+ | +----------------+
| | |
+--------->+ 右 值 |
| |
+----------------+

表达式一定是左值,亡值,右值中的一种。

左值等具体代码可参考基础类别

具体的定义可以参考n3055