在现代 C++ 中,一个表达式有两个独立维度的属性:
- 拥有身份(Has Identity):在内存中有固定的、可寻址的物理地址。
- 可被移动(Can be Moved):生命周期即将结束,它的资源(比如堆内存)允许被别人无损接管。
根据这两个维度的组合,C++ 官方将所有的“值(Value)”严格划分为了三大主类:
┌──────────────────┐
│ 表达式(Value) │
└────────┬─────────┘
▼
┌─────────────────┴─────────────────┐
▼ ▼
【有身份 / 内存可寻址】 【临时数据 / 可被移动】
(Has Identity) (Can be Moved)
│ │
┌─────┴───────────┐ ┌─────┴───────────┐
▼ ▼ ▼ ▼
左值 (lvalue) 将亡值 (xvalue) 将亡值 (xvalue) 纯右值 (prvalue)
1. 左值 (lvalue) —— 有身份,不可移动
- 本质:指代一个持久存在的内存对象。只要你能用
&(取地址符)拿到它在内存中的物理地址,它就是左值。 - 特点:有名字,生命周期由作用域决定,编译器不能随意销毁它。
- 常见示例:变量名
a、函数名、左值引用int& r = a;、解引用表达式*ptr。
2. 纯右值 (prvalue) —— 无身份,可移动
- 本质:纯粹的临时字面量或运算产生的临时过渡数据。它们在物理内存中没有独立的持久身份(你没办法对它们取地址)。
- 特点:没有名字,生命周期极短,通常只存在于 CPU 寄存器或编译器的临时栈帧中。
- 常见示例:字面量
42、'a';算术表达式a + b;函数返回的临时非引用对象(如std::string("hello"))。
3. 将亡值 (xvalue) —— 有身份,可移动(现代 C++ 零拷贝的精髓 ✨)
- 本质:这是一个虽然在内存中有地址,但它马上就要被销毁(即将死亡)的对象。
- 特点:因为它有身份,所以你可以找到它;因为它可移动,这意味着你被允许去“合法压榨”它,把它的堆内存指针据为己有。
- 常见示例:最典型的将亡值就是通过
std::move(a)强转出来的结果,或者返回右值引用的函数调用。