附录
附录 A 单一定义规则
A.1 翻译单元
A.2 声明和定义
A.3 单一定义规则细节
A.3.1 对每个程序的约束
A.3.2 对每个翻译单元的约束
A.3.3 跨翻译单元对等约束
附录 B 值类别
表达式是 C++ 语言的基石,它提供了表达计算的主要机制。每个表达式都有一个类型,它描述了其计算产生的值的静态类型。表达式7
具有类型int
,表达式5 + 2
也是,表达式x
也是,如果变量x
的类型是int
。每一个表达式还有一个值类别,它描述了值是如何形成的,并影响表达式的行为。
B.1 传统的左值和右值
历史上,曾经只有两种值类别:即左值和右值。左值是引用存储在内存或机器寄存器中的实际值的表达式,例如表达式x
,其中x
是变量的名称。这些表达式可以修改,允许更新存储的值。例如,如果x
是int
类型的变量,下面的赋值将把x
的值替换为7
:
x = 7;
术语左值来源于这些表达式在赋值操作中可能扮演的角色:字母“l”代表“左手边”,因为(历史上,在 C 语言中)只能在赋值操作的左手边出现左值。相反,右值(“r”代表“右手边”)只能出现在赋值表达式的右侧。
然而,当 C 语言在 1989 年标准化时,情况发生了变化:虽然int const
仍然是存储在内存中的值,但它不能出现在赋值操作的左侧:
int const x; // x 是一个不变的左值
x = 7; // ERROR: 可修改的左值才能在左侧
C++ 做了进一步的改变:类的右值可以出现在赋值的左边。这种赋值实际上是对类的适当赋值操作符的函数调用,而不是对标量类型的“简单”赋值,因此它们遵循成员函数调用的(单独的)规则。
由于所有这些变化,术语左值现在有时表示可本地化的值。引用变量的表达式并不是左值表达式的唯一类型。另一类左值表达式包括指针解引用操作(例如 *p),它指向存储在指针引用的地址上的值,以及指向类对象成员的表达式(例如:p->data
)。即使调用返回用 & 声明的“传统”左值引用类型的值的函数也是左值。例如(详情见 679 页 B.4 节):
std::vector<int> v;
v.front(); // 仍然是左值,因为返回类型是一个左值引用
也许令人惊讶的是,字符串字面值也是(不可修改的)左值。
右值是纯数学值(如7
或字符'a'
),不一定有任何相关的存储空间:它们的存在是为了计算的目的,但是一旦它们被使用,就不能再被引用。特别是,除了字符串字面值(例如:7
,'a'
,true
,nullptr
)之外的任何文字值都是右值,许多内置算术计算的结果(例如:整数类型x
的x + 5
)和调用按值返回结果的函数也是右值。也就是说,所有临时值都是右值。(但这不适用于引用它们的命名引用。)