C++11中对LValue和RValue的界定更加详细而合理(但是也更加麻烦了)
1章节:C++11的新分类介绍
2章节:不同值之间的转换
3章节:对一些常见的表达式,如何区分他们是什么值?
1. 表达式分类
所谓Value,是对表达式而言的。一个表达式可以分为以下几种Value,下边详细说明
1.1. lvalue(左值)
lvalue指代一个函数或者对象。例如:
- E是指针,则*E是lvalue
- 一个函数的返回值是左值引用,其返回值是lvalue。例如int& foo();
1.2. xvalue(expiring value,临终值)
xvalue指代一个对象,但是和lvalue不同,这个对象即将消亡。具体来说,xvalue是包含了右值引用的表达式。因为右值引用是C++11新引入的东西,所以xvalue也是一个新玩意。例如:
- 一个函数的返回值是右值引用,其返回值是xvalue。例如int&& foo();
1.3. glvalue(generalized lvalue,泛左值)
glvalue即lvalue和xvalue的统称。
1.4. rvalue(右值)
rvalue是xvalue和prvalue的统称。因为引入了右值引用,rvalue的定义在C++中被扩大化了。
1.5. prvalue(pure rvalue,纯右值)
prvalue指代一个临时对象、一个临时对象的子对象或者一个没有分配给任何对象的值。prvalue即老标准中的rvalue。例如:
- 一个函数的返回值是平常类型,其返回值是rvalue。例如int foo();
- 没有分配给任何对象的值。如5.3,true。
2. 表达式值类型的转换
glvalue → prvalue
其实表达式的转换,只有以上一途,也就是
- lvalue → prvalue
- xvalue → prvalue
2.1. lvalue → prvalue
这个很常见:glvalue(lvalue和xvalue)可以隐式转换为prvalue来满足需求。例如:
int& foo(int val) // 函数接受prvalue { int a = val + 1; return a; } int main() { int i = 5; cout << foo(4) << endl; // 传递进去一个prvalue cout << foo(i) << endl; // 传递进去一个lvalue cout << foo(foo(i)) << endl; // foo(i)返回lvalue,传递至外层函数 system("pause"); }
当然,这个函数写的不大好,不应该将临时对象传递出函数的~
如果将foo改写为下面这样,上边的代码就不能工作了,因为prvalue→lvalue是不成滴~
int& foo(int val) { return val + 1; }
2.2. cv-qualifier的解除
在其他转换中,const转换是不能去除的,但是glvalue到prvalue的转换是可以去除的。这个说辞比较晦涩,其实十分常见,比如:
const int a = 5; int b = a; // b = ? 这个需要右值,所以把a先转为右值,同时丢弃了const限定词
3. 表达式值类型的判定
C++的每一个表达式都有其值类型,换句话说,任何表达式,都属于以下三者之一:lvalue,xvalue,prvalue。
如果是简单的表达式自然很好理解,如上一节所举的变量、常量以及返回值。但是大部分情况都要复杂得多。
2.1. C++内建操作的判定
这个就要具体操作具体分析了。C++的内建操作对操作数的类型做出了详细的界定,对整个操作代表的类型也做出了详细的界定。例如:
分配运算 op1 = op2。op1是lvalue,op2是rvalue。整个操作是lvalue
2.2. 用户定义的操作
用户定义操作,无非就是自建函数。(恩,没错,C++的操作符重载也是函数重载~~)操作数等价于函数参数,操作yield值相当于函数返回值。其类型都由用户界定。例如:
- int foo(int); 参数是prvalue,返回值是prvalue。
- int& foo(int); 参数是prvalue,返回值是lvalue。u