最可靠判断法是看能否对表达式取地址:&x合法则为左值,&(a+b)非法则为右值;左值有身份可寻址,右值为临时值不可取地址,将亡值是可移动的右值子类。

左值和右值不是“变量类型”,而是表达式的值类别(value category)——同一个变量在不同上下文中可能是左值,也可能是右值;判断依据不是它叫什么,而是它“此时能不能取地址”“此时有没有名字”“此时生命周期是否还受控”。
怎么快速判断一个表达式是左值还是右值?
最可靠、最实用的方法:看能不能对它写 & 取地址。
-
&x编译通过 →x是左值(比如变量名、arr[0]、*p、返回T&的函数调用) -
&(a + b)编译失败 →a + b是右值(临时计算结果) -
&42或&"hello"都错 → 字面量是右值(注意:"hello"是数组,有地址,但 C++ 中字符串字面量本身是左值;不过它不可修改,属于常量左值,这点容易混淆,先按“不能赋值+不可取地址的临时量”归为右值思维更安全)
将亡值(xvalue)到底是什么?
将亡值不是新造出来的“第三种值”,而是右值的一种子类,特指“本是左值,但被显式标记为‘可被移动’”的表达式。它的存在只为支持移动语义。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
- 典型来源:
std::move(x)返回的就是 xvalue;static_cast<t>(x)</t>也是 - 关键特征:有名字(比如
std::move(x)这个调用本身有名字),但语义上“允许被移动”,编译器会优先调用移动构造/移动赋值 - 常见误判:认为
std::move(x)把x变成了右值 —— 实际上它返回的是一个 xvalue(属于右值范畴),但x本身仍是左值,只是你“承诺不再用它”,后续访问x是未定义行为
为什么 T&& 既能绑定右值,也能绑定左值?
因为 T&& 不是“右值引用”的简单等价物,而是“万能引用(universal reference)”,其绑定行为取决于模板推导规则(即所谓的“引用折叠”)。
- 当
foo(x)调用时,x是左值 → 模板参数T推导为int&→T&&折叠为int&(左值引用) - 当
foo(42)调用时,42是右值 →T推导为int→T&&就是int&&(右值引用) - 所以真正决定绑定能力的,是模板参数推导 + 引用折叠,不是
&&符号本身
最容易忽略的一点:值类别是表达式级别的属性,不是变量或类型的固有属性;std::move 不改变对象本身,只改变表达式的值类别;而移动后原对象进入有效但未指定状态——这正是很多人踩坑的根源:以为“移走了就安全了”,其实继续读 x 仍可能 crash 或返回垃圾值。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










