C对于右值的定义是表达式的值,C中所有完整表达式的结果都是右值。所谓完整表达式(full expression),指的是这样的表达式,它不是其它表达式或声明符的一部分。包括条件表达式和逗号表达式等等都不产生左值,而子表达式计算产生的中间结果或临时对象,很多人以为都是右值,但实际上,它们不一定是右值。例如:
int a[ 5 ] = { 1, 2, 3, 4, 5 };
int *p = a;
*p = 2; /* A */
a[ 1 ] = 3; /* B */
*p; /* C */
a[ 1 ]; /* D */
A和B中的*p和a[ 1 ]都是内置赋值运算符的子表达式,虽然都是中间结果,但显然都属于左值表达式;只有当它们作为完整表达式时,如C和D中的*p和a[ 1 ]所示,都会进行最后的左值转换,使得结果皆为右值。这个现象的本质,是由于C将所有完整表达式的结果一律进行最后的从左值到右值的转换,这个行为可以理解为一个完全求值的过程(完全求值不是标准术语)。
但C++的完整表达式并不要求进行完全求值,是否保留左值性视需要而定,这个“需要”是什么?其实是C++某些运算符的强制规定,例如内置赋值运算符、前置增量和前置减量运算符等等,这类运算符的结果被强制规定为左值,对于条件运算符,只有第二和第三表达式皆为左值且类型相同时才保证结果为左值。原因无它,仅仅规定而已。
右值是不是对象?由于左值要求一个对象,因此比较容易产生的误解是,右值不是对象。但,如果函数返回一个结构呢?
struct S { int a; };
struct S fun( void ){ struct S s; ...; return s }
由于POD结构属于聚集,不是标量,因而POD结构不是一个值,标量才被视为一个值。上述代码中,fun返回了一个POD结构S的临时对象,而且是一个右值,即无论C还是C++都存在右值对象,。因此,右值只是不要求是对象,并非不能是对象。