Value Categories
在 C++98/03 时代,表达式分为两类
- 左值(
lvalue) - 右值(
rvalue)
C++11 为了支持移动语义,把值类别细分成五种:
- 左值(
lvalue) - 右值(
rvalue) - 纯右值(
prvalue) - 将亡值(
xvalue) - 广义左值(
glvalue)
具体关系如下:glvalue 由 lvalue 和 xvalue 组成,rvalue 由 xvalue 和 prvalue 组成。xvalue 同时属于两边。
- 左值(
lvalue):具有标识(identity)的表达式,例如有名字,通常可以取地址。 - 将亡值(
xvalue):具有标识、且资源可以被移动和复用的表达式,通常所指对象即将结束生命周期。 - 纯右值(
prvalue):没有标识的右值,不能取地址,可以移动。
为了支持拷贝省略(copy elision),C++17 重新定义了广义左值和纯右值:
- 广义左值(
glvalue):求值结果是确定某个对象、位域或函数的标识。对象和函数通常有内存地址,位域没有独立地址。 - 纯右值(
prvalue):求值的目的是初始化某个对象或位域,或计算运算符操作数的值。
C++17 规定,类或数组类型的 prvalue 不需要先创建临时副本(temporary copies)。用它初始化目标时,直接在目标地址构造。期间没有拷贝也没有移动,因此该类型不必支持这些操作,编译器可以安全地去掉这些步骤。这种直接在目标处初始化发生在以下两种场景:
- 函数返回一个 prvalue。
- 用 prvalue 初始化某个对象。
不过以下情况仍然会创建临时对象:
- 将 prvalue 绑定到引用。
- 访问 prvalue 对象的成员变量。
- 对数组类型的 prvalue 进行下标访问。
- 数组类型的 prvalue 退化为指针。
- 对类类型的 prvalue 进行派生类到基类的转换。
- prvalue 本身作为弃值表达式(
discarded-value expression)出现时,例如单独写T();,通常是为了构造函数或析构函数的副作用。