expression
├── glvalue
│ ├── lvalue
│ └── xvalue ──┐
└── rvalue │
├── xvalue ──┘
└── prvalue
关键点:上面两处 xvalue 是同一个类别,不是两个不同的东西 。它是 glvalue 和 rvalue 的交集------从"有没有 identity"这个维度看,它属于 glvalue;从"能不能被移动"这个维度看,它又属于 rvalue。所以真正互斥、构成完整划分的"基本"类别只有三个:lvalue、xvalue、prvalue(正是你最后总结的"左值、纯右值、将亡值"),而 glvalue 和 rvalue 只是这三者的两种组合方式:
- glvalue = lvalue ∪ xvalue("generalized lvalue",广义左值)
- rvalue = xvalue ∪ prvalue
标准(basic.lval)实际上是用两个正交属性定义出这三个基本类别的:
| 可被移动 | 不可被移动 | |
|---|---|---|
| 有 identity | xvalue(将亡值) | lvalue(左值) |
| 无 identity | prvalue(纯右值) | 不存在这种表达式 |
("有 identity"指的是能否判断两次求值是不是指向同一个实体,比如能不能取地址、比较地址。)
举几个例子对应一下:
- lvalue :变量名
x,解引用*p,下标a[0],返回T&的函数调用 - xvalue :
std::move(x),返回T&&的函数调用,static_cast<T&&>(x) - prvalue :字面量
42、3.14,返回非引用类型T的函数调用,甚至this指针本身(虽然它指向的对象有 identity,但this这个表达式本身是指针值,属于 prvalue;*this才是 lvalue)
结论:
三分类是 lvalue / xvalue / prvalue,glvalue 和 rvalue 是上层的两种组合视角。