左值和右值
左值与右值这两个概念是从 C 中传承而来的,左值指既能够出现在等号左边,也能出现在等号右边的变量;右值则是只能出现在等号右边的变量。
左值和右值在 C/C++ 中是表达式(expression)的一种属性,被称为值类别(value categories)。 表达式是指由运算符(operator)和运算对象(operand)构成的计算式。 表达式是可求值的,对表达式求值将得到一个结果,这个结果有两个属性:类型和值类别。
C++支持移动语义后,值类别被用来描述表达式的两种核心特性:身份和可移动性
- 是否具有身份:是否指代一个对象,即是否可以取地址。
- 是否可被移动:是否具有移动构造、移动赋值等函数,让我们有办法利用这些临时对象。
按上面两个特性,值类别可分为:
- 左值(lvalue):有身份,不可移动。
- 将亡值(xvalue):有身份,可被移动。
- 纯右值(prvalue:无身份,不可移动。
也可分为:
- 泛左值(glvalue):有身份(左值和将亡值)。
- 右值(rvalue):可移动(右值和将亡值)。
严格来讲,左值是表达式的结果的一种属性,但更为普遍地,我们通常用左值来指代左值表达式。所谓左值表达式,就是指求值结果的值类别为左值的表达式。通常我们无需区分左值指的是前者还是后者,因为它们表达的是同一个意思,不会引起歧义。对于纯右值和将亡值,亦然。
左值
左值具有以下特征:
- 可通过取地址运算符&获取其地址。
常见的左值举例:
- 变量名
- 函数(实际是函数指针)
- 返回左值引用的函数调用
- 前置自增/自减运算符连接的表达式(++i/--i)(先将i加1,再返回i的引用)
- 由赋值运算符或复合赋值运算符连接的表达式(a=b、a+=b、a%=b)(表达式计算完还是a)
- 解引用表达式(*ptr)
- 字符串字面值("abc")(可取地址)
- 具名的右值引用
纯右值
满足下列条件之一:
- 除字符串字面值以外的字面值
- 求值结果为字面值的表达式
- 不具名的临时对象
常见的纯右值举例:
- 除字符串字面值以外的字面值
- 返回非引用类型的函数调用
- 后置自增/自减运算符连接的表达式(i++/i--)(先创建临时对象,再对i+1,再返回临时对象)
- 算术表达式(a+b、a&b、a<<b)
- 逻辑表达式(a&&b、a||b、~a)
- 比较表达式(a==b、a>=b、a<b)
- 取地址表达式(&a)
将亡值
C++11之前的右值和C++11中的纯右值是等价的。C++11中的将亡值是随着右值引用的引入而新引入的。换言之,“将亡值”概念的产生,是由右值引用的产生而引起的,将亡值与右值引用息息相关。所谓的将亡值表达式,就是下列表达式:
- 返回右值引用的函数的调用表达式
- 转换为右值引用的转换函数的调用表达式 以上二者返回的都是不具名的右值引用。
举例:
- std::move(val)
- static_cast<T &&>(val)或- (T&&) val
将亡值代表一个即将被销毁的对象,它的资源可以被安全地转移。