在c++11以后,表达式按值类别,可分为3种:
- 左值(left value,lvalue)
- 将亡值(expiring value,xvalue)
- 纯右值(pure rvalue,pralue)
另,
泛左值(generalized lvalue,glvalue) = 左值 + 将亡值
右值(right value,rvalue) = 纯右值 + 将亡值
以下详细讲解:
左值
左值:能够用&取地址的表达式是左值表达式。
- 函数名 (实际上是函数指针)
- 变量名(指的是是具名变量,如: std::cin、std::endl等)
- 返回左值引用的函数调用,如: std::getline(cin, str), cout << 1
- 前置运算表达式,如 ++i, --i
- 由赋值运算符或复合赋值运算符连接的表达式,如: a=b, a+=b,、a%=b
- 解引用表达式 *p
- 字符串字面值"abc"
- 具名的右值引用
这里解释一下“具名的右值引用是左值”。看下面的代码:
void foo(X&& a)
{
X b = a;
a.data += 1; // 仍然可以访问a
}
这里 a 是一个具名的右值引用,在 “X b=a;” 这一句,因为a是左值,所以调用的是X的拷贝构造函数而不是移动构造函数,因此此句之后对a进行访问是没有问题的。而这就是“具名的右值引用是左值”的意义所在。
纯右值
纯右值:纯粹的字面值(如10,True),或者是 求值结果相当于字面值(除字符串)或不具名的临时对象。
- 字面值(除字符串字面值以外),如: 40, true, nullptr
- 返回值不是引用类型的函数调用,如: str.substr(1, 2), 2+2
- 后置运算符表达式,如 i++, i–
- 算术表达式,如: a+b, a&b, a<<b
- 逻辑表达式,如: a&&b, a||b, ~a
- 比较表达式,如: a==b, a>=b, a<b
- 取地址表达式,即 &a
- lambda表达式,如: [](int x){return x*x;}
- 转换目标为非引用类型的cast表达式
将亡值
在C++11之前的右值和C++11中的纯右值是等价的。C++11中的将亡值是随着右值引用(即像int && a 这样的)而引入的。
所谓的“将亡”,当一个右值准备完成初始化或赋值任务时,它就将亡了。
将亡值表达式,就是:
- 返回右值引用的函数的调用表达式
- 转换为右值引用的转换函数的调用表达式
以上二者返回的都是不具名的右值引用。
以下是将亡值:
- std::move(val)
- static_cast<X &&>(val) 或 (T&&)val
- 不具名的右值引用(包括以上二者)
参考文献
- http://naipc.uchicago.edu/2015/ref/cppreference/en/cpp/language/value_category.html
- https://en.cppreference.com/w/cpp/language/value_category
- https://www.cnblogs.com/zpcdbky/p/5275959.html
(完)