左值与右值这两个概念是从 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中,我们用左值去初始化一个对象或为一个已有对象赋值时,会调用拷贝构造函数或拷贝赋值运算符来拷贝资源;而当我们用一个右值(包括纯右值和将亡值)来初始化或赋值时,会调用移动构造函数或移动赋值运算符来移动资源,从而避免拷贝,提高效率。当该右值完成初始化或赋值的任务时,它的资源已经移动给了被初始化者或被赋值者,同时该右值也将会马上被销毁(析构)。也就是说,当一个右值准备完成初始化或赋值任务时,它已经“将亡”了。