左值和右值
左值与右值这两个概念是从 C 中传承而来的,左值指既能够出现在等号左边,也能出现在等号右边的变量;右值则是只能出现在等号右边的变量。
左值和右值在 C/C++ 中是表达式(expression)的一种属性,被称为值类别(value categories)。 表达式是指由运算符(operator)和运算对象(operand)构成的计算式。 表达式是可求值的,对表达式求值将得到一个结果,这个结果有两个属性:类型和值类别。
C++11后则对值类别进行了重新定义。表达式的值类别必然属于三种基本值类别之一:
- 左值(lvalue)
- 将亡值(xvalue)
- 纯右值(prvalue)
在此之上,C++ 还引申出了两种混合值类别:
- 泛左值(glvalue),包含左值与将亡值
- 右值(rvalue),包含纯右值和将亡值
严格来讲,左值是表达式的结果的一种属性,但更为普遍地,我们通常用左值来指代左值表达式。所谓左值表达式,就是指求值结果的值类别为左值的表达式。通常我们无需区分左值指的是前者还是后者,因为它们表达的是同一个意思,不会引起歧义。对于纯右值和将亡值,亦然。
左值
左值具有以下特征:
- 可通过取地址运算符
&
获取其地址
常见的左值举例:
- 函数名和变量名
- 返回左值引用的函数调用
- 前置自增/自减运算符连接的表达式
(++i/--i)
- 由赋值运算符或复合赋值运算符连接的表达式
(a=b、a+=b、a%=b)
- 解引用表达式
(*ptr)
- 字符串字面值
("abc")
- 具名的右值引用
纯右值
满足下列条件之一:
- 除字符串字面值以外的字面值
- 求值结果为字面值的表达式
- 不具名的临时对象
常见的纯右值举例:
- 除字符串字面值以外的字面值
- 返回非引用类型的函数调用
- 后置自增/自减运算符连接的表达式
(i++/i--)
- 算术表达式
(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
关于将亡值的进一步说明: 在C++11中,我们用左值去初始化一个对象或为一个已有对象赋值时,会调用拷贝构造函数或拷贝赋值运算符来拷贝资源;而当我们用一个右值(包括纯右值和将亡值)来初始化或赋值时,会调用移动构造函数或移动赋值运算符来移动资源,从而避免拷贝,提高效率。当该右值完成初始化或赋值的任务时,它的资源已经移动给了被初始化者或被赋值者,同时该右值也将会马上被销毁(析构)。也就是说,当一个右值准备完成初始化或赋值任务时,它已经“将亡”了。