您的位置:首页 > 编程语言 > C语言/C++

c++ -- value category

2015-12-04 23:48 393 查看

c++ – value category

说到 value category ,可能大家都不知道是啥。但是说到左值,右值,可能很多人就听说了。C++将一个值作为左值、右值的分类称作 value category。

说到 value category ,首先要强调一点,就是 value category 是值的属性,不是变量的属性。所谓值,就是表达式的结果,也就是说着也是表达式结果的一个属性。同时,表达式对其子表达式的结果的 value category 可能会有一定要求。因而说到左值右值,必须明确得到这个值的表达式,或者使用这个值的表达式,否则没有意义。

另外,这里所说的值可以是一个函数(函数本身,不是函数指针)。

value category

C++11 的 value category 其实并不只有左值、右值。C++11 的每一个值可以分为三类:lvalue, xvalue(eXpiring value), prvalue(pure rvalue)。然后,lvalue 与 xvalue 合称 glvalue (general lvalue);xvalue 与 prvalue 合称 rvalue。

对各类的简要说明如下:

lvalue: 一个对象

xvalue: 也是一个对象,通常已经接近生存期的终点

glvalue: lvalue 与 xvalue

rvalue: xvalue, 临时对象,或前面两者的子对象,或没有对象的值

prvalue: 不是 xvalue 的 rvalue 。

仅依靠上面的说明来区分 value category 似乎有些困难,不过标准也没有期望能通过以上的说明区分不同 value category。标准采用的另一种方式定义 value category ,及明确的规定了每一种表达式的结果的 value category 。同时说明的每一种表达式所期望的操作数的 value category (对于操作数,没有说明的,就是 prvalue)。

下面简单介绍一下:

表达式的value category

在介绍表达式的 value category 之前,首先要说明一点,就是所有被重载过的操作符,均按照函数调用处理,他们的结果的 value category 参照函数调用结果的 value category 处理。

多数表达式,如:
a++
,
~a
,
a+b
,
&a
this
,lambda 表达式,
T()
T{...}
T(v1,v2, ...)
等,会产生一个 prvalue,除了以下情况:

literal 字面量 字符串常量为 lvalue 。其余常量为 prvalue。

(expression)
与 expression 相同(即,括号不影响 value category)

id-expression:

identifier
:函数、变量、数据成员为 lvalue ,其余为 prvalue

[注:函数的引用作为函数处理。对变量的左值引用与右值引用均为变量,因而他们都是 lvalue 。]

class::identifier
: 静态成员函数与数据成员为 lvalue ,其它为 prvalue。

namespace::identifier
:函数与变量为 lvalue ,其余为 prvalue

enumeration::enumerator
: prvalue

subscribe
A
: 其中一个操作数需要具有数组或指针类型。如果这个操作数是指针,结果为 lvalue;如果为数组,那么如果数组为lvalue,结果也是 lvalue,否则结果为 xvalue。

Function call:
func(...)
类型转换:
dynamic_cast<T>(v)
static_cast<T>(v)
reinterpret_cast<T>(v)
const_cast<T>(v)
,
(T)v
T(v)


返回值为 lvalue reference ,函数的 rvalue reference:lvalue

返回值为其它 rvalue reference :xvalue

其它:prvalue

类成员访问:
E1.E2


E2 为引用、静态成员变量:lvalue

E1为 lvalue ,E2 为非静态成员变量: lvalue

E1为 rvalue ,E2 为非静态成员变量: xvalue

E2 为静态成员函数:lvalue

E2 为非静态成员函数:prvalue

E2 为enumerator: prvalue

typeid()
: lvalue

*p
: lvalue

**
++
,
--
(前缀) : lvalue

E1.*E2


E2 是 pointer to data member, 若 E1 为 lvalue ,结果为 lvalue ,否则结果为 xvalue

E2 是 pointer to member function,结果为 prvalue

E1->*E2
: 按
(*(E1)).*E2
处理

E1?E2:E3
如果 E2与E3类型与 value category 均相同,则结果的 value category 与 E2、E3相同。否则结果为 prvalue

a = b
,
a op= b
: lvalue

E1,E2
: value category 与 E2 相同

左值引用与右值引用

引用需要绑定到一个表达式的值上。左值引用与右值引用的区分在于,他们所能绑定到的值的 value category 不同。

一个简单的原则是,非 const 的左值引用需要绑定到一个左值,右值引用需要绑定到一个右值。

注意,左值引用或右值引用的变量(非函数引用)作为表达式出现的时候,结果都是一个左值。他们的区别体现在他们所能绑定到的对象上。

std::move
std::forward

[这一部分不考虑函数的问题。]

从上面的介绍可以看到,一个变量,无论他是什么类型,即使它是一个右值引用,它独立形成一个表达式的时候,总是一个左值。那么如果想得到一个右值怎么办呢?这就是
std::move(v)
的作用。它将生成它的参数的一个右值。其实做法很简单,
static_cast<remove_reference<T>&&>(v);
。上面说了,类型转换生成一个右值引用时,结果为 xvalue ,是一个右值。

std::forward
的作用则是,为右值引用生成一个右值,而对其它则保留为一个左值。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: