cppreference Value categories
Each C++ expression (an operator with its operands, a literal, a variable name, etc.) is characterized by two independent properties: a type and a value category. Each expression has some non-reference type, and each expression belongs to exactly one of the three primary value categories: prvalue, xvalue, and lvalue.
NOTE: 始终牢记stroustrup “New” Value Terminology中给出的"W"图
iM im Im \ / \ / \ / \ / \ / \ / i m
lvalue xvalue prvalue \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / glvalue rvalue
需要按照stroustrup “New” Value Terminology 中的思路来理解本文。
Primary categories
lvalue
The following expressions are lvalue expressions:
Named variable
NOTE: 这种是最容易理解的,它们是典型的
iM
the name of a variable, a function, a template parameter object (since C++20), or a data member, regardless of type, such as std::cin or std::endl. Even if the variable's type is rvalue reference, the expression consisting of its name is an lvalue expression;
NOTE: 上面这段话后半句的意思是: 虽然 std::cin or std::endl 的类型是rvalue reference,但是它们满足
iM
,因此它们是lvalue,这就是stackoverflow What are move semantics?#
part two 中总结的:A named rvalue reference is an lvalue, just like any other variable.
在
C++\Language-reference\Reference\Move-semantic\stackoverflow-What-is-move-semantics.md
中收录了这篇文章。需要注意的是: function是lvalue,关于此,参见
C++\Language-reference\Functions\Function.md
。
Lvalue function call expression
NOTE: 本节标题中的**lvalue function**是我创造的一个概念,与它相对的一个概念是**rvalue expression**,下面是它们的定义:
概念 说明 lvalue function function whose return type is lvalue reference rvalue function function whose return type is non-reference 上述 两个概念是基于value category来对function进行分类的,这样做是为了便于描述value category,因为在C++中,operator可以使用function来进行描述(参见
C++\Language-reference\Expressions\Operators\index.md
中提出的“Operator is a kind of function”),所以operator expression(表示的是包含operator的expression) 都可以使用function call expression来进行描述,所以使用function call expression,可以描述所有的operator expression。下面是我总结的统一的规则:
expression value category 说明 Lvalue function call expression lvalue 这种情况是非常具有代表性的,后面的很多operator expression都可以归入这个范畴 Rvalue function call expression rvalue
a function call or an overloaded operator expression, whose return type is lvalue reference, such as std::getline(std::cin, str), std::cout << 1
, str1 = str2
, or ++it
;
NOTE: 需要对上面列举的例子进行详细的说明:
std::getline
的原型如下,显然返回值的类型是lvalue reference:template< class CharT, class Traits, class Allocator > std::basic_istream<CharT,Traits>& getline( std::basic_istream<CharT,Traits>& input, std::basic_string<CharT,Traits,Allocator>& str, CharT delim );
std::cout
<< 1
,它实际调用的是std::basic_ostream::operator<< ,这个函数的原型是:basic_ostream& operator<<( int value );
str1 = str2
是 assignment expression,下面会进行介绍;
++it
是 increment expression,下面会进行介绍;
Assignment expression
a = b
, a += b
, a %= b
, and all other built-in assignment and compound assignment expressions;
NOTE: 在 assignment and compound assignment 中,给出了Assignment operators的函数原型,可以看到,它们都是前面描述的: a function call or an overloaded operator expression, whose return type is lvalue reference,即它是lvalue function,所以包它的expression就是lvalue expression。
Pre-increment and pre-decrement expression
++a
and --a
, the built-in pre-increment and pre-decrement expressions;
NOTE: 相当于 Lvalue function call expression。
Indirection expression
*p
, the built-in indirection expression;
NOTE: 相当于 Lvalue function call expression。
最最典型的例子就是:
*this
,参见 cppreference Non-static member functions ;
Built-in subscript
a[n]
and p[n]
, the built-in subscript expressions, where one operand in a[n]
is an array lvalue (since C++11);
NOTE: 相当于 Lvalue function call expression。
Member access expression
NOTE: 本段标题中的“member access”是取自cppreference Member access operators,下面这些operator,cppreference中将它们都归入了Member access operators。
a.m
, the member of object expression, except where m
is a member enumerator or a non-static member function, or where a
is an rvalue and m
is a non-static data member of non-reference type;
NOTE: 需要注意的是,“member of object”的意思是“object的member”。下面对否定条件进行说明:
否定条件 分析 member enumerator enumerator是prvalue non-static member function function肯定是右值
p->m
, the built-in member of pointer expression, except where m
is a member enumerator or a non-static member function;
NOTE: 和前面的情况类似
a.*mp
, the pointer to member of object expression, where a
is an lvalue and mp
is a pointer to data member;
p->*mp
, the built-in pointer to member of pointer expression, where mp
is a pointer to data member;
Comma expression
a, b
, the built-in comma expression, where b
is an lvalue;
a ? b : c
, the ternary conditional expression for some b
and c
(e.g., when both are lvalues of the same type, but see definition for detail);
String literal
a string literal, such as "Hello, world!";
Cast expression
a cast expression to lvalue reference type, such as static_cast<int&>(x)
;
Properties:
- Same as glvalue (below).
- Address of an lvalue may be taken: &++i[1] and &std::endl are valid expressions.
- A modifiable lvalue may be used as the left-hand operand of the built-in assignment and compound assignment operators.
- An lvalue may be used to initialize an lvalue reference; this associates a new name with the object identified by the expression.
prvalue
The following expressions are prvalue expressions:
Literal类prvalue
NOTE: 本段标题“Literal类prvalue”是我创造的一个概念,它表示类似于literal的prvalue,包含如下:
literal:
a literal (except for string literal), such as 42
, true
or nullptr
;
NOTE: literal是非常典型的一类prvalue,后面的很多都可以归入这一类,literal是典型的与named variable不同的;我们可以使用传统的rvalue来理解它: 显然它是无法处于assignment左侧的。
this
:
the this
pointer;
NOTE: 需要注意的是,
this
pointer不是一个variable(虽然this
是有name的,我们不需要declare),它相当于literal(可以使用nullptr
来进行类比),显然它是无法处于assignment左侧的;如果我们从
this
的实现来思考的话,那么this
pointer是rvalue就非常容易理解: compiler让this
的值为object的地址,因此,this
仅仅是一个地址值而已,和literal非常类似,所以this
是prvalue。
enumerator :
an enumerator
NOTE: enumerator和
this
、literal是非常类似的,enumerator的值在compile阶段就已经确定了,所以所有的enumerator都会被替换为它的对应的value;显然它是无法位于assignment左侧的。
Non-type template parameter :
non-type template parameter unless its type was a class or (since C++20) an lvalue reference type;
NOTE: non-type template parameter对应的argument一般是literal
Rvalue function call expression
NOTE: Rvalue function 的概念在前面的Lvalue function call expression章节中已经介绍了
a function call or an overloaded operator expression, whose return type is non-reference, such as str.substr(1, 2)
, str1 + str2
, or it++
;
NOTE: 下面是cnblogs Lvalues and Rvalues中给出的一个例子:
int a; a = 10; // 10是rvalue,它没有地址,&10就是错误的表达式。从汇编语言的角度来看,10是直接存在于MOV指令中的立即数。 10 = a; // 错误,10是rvalue,不可赋值。 //函数返回值属于rvalue,因为返回值通常用CPU寄存器传递,没有地址。 int foo() { return 0; } int b = foo(); //没问题,函数返回值是rvalue。 int* p = &foo(); //错误,rvalue没有地址。 void bar(int& i) { } bar(foo()); //错误,bar函数参数需要的是lvalue。
Post-increment and post-decrement expression
a++
and a--
, the built-in post-increment and post-decrement expressions;
NOTE: 相当于 Rvalue function call expression。
Arithmetic expression
a + b
, a % b
, a & b
, a << b
, and all other built-in arithmetic expressions;
Logical expression
a && b
, a || b
, !a
, the built-in logical expressions;
Comparison expression
a < b
, a == b
, a >= b
, and all other built-in comparison expressions;
Address-of
&a
, the built-in address-of expression;
Member of object expression
a.m
, the member of object expression, where m
is a member enumerator or a non-static member function[2], or where a
is an rvalue and m
is a non-static data member of non-reference type (until C++11);
p->m
, the built-in member of pointer expression, where m
is a member enumerator or a non-static member function[2];
a.*mp
, the pointer to member of object expression, wheremp
is a pointer to member function[2], or wherea
is an rvalue andmp
is a pointer to data member (until C++11);p->*mp
, the built-in pointer to member of pointer expression, wheremp
is a pointer to member function[2];a, b
, the built-in comma expression, whereb
is an rvalue;a ? b : c
, the ternary conditional expression for someb
andc
(see definition for detail);- a cast expression to non-reference type, such as
static_cast<double>(x)
, std::string{}, or(int)42
;
Properties:
- Same as rvalue (below).
- A prvalue cannot be polymorphic: the dynamic type of the object it identifies is always the type of the expression.
- A non-class non-array prvalue cannot be cv-qualified. (Note: a function call or cast expression may result in a prvalue of non-class cv-qualified type, but the cv-qualifier is immediately stripped out.)
- A prvalue cannot have incomplete type (except for type void, see below, or when used in decltype specifier)
- A prvalue cannot have abstract class type or an array thereof.
xvalue
The following expressions are xvalue expressions:
- a function call or an overloaded operator expression, whose return type is rvalue reference to object, such as std::move(x);
a[n]
, the built-in subscript expression, where one operand is an array rvalue;a.m
, the member of object expression, wherea
is an rvalue andm
is a non-static data member of non-reference type;a.*mp
, the pointer to member of object expression, wherea
is an rvalue andmp
is a pointer to data member;a ? b : c
, the ternary conditional expression for someb
andc
(see definition for detail);- a cast expression to rvalue reference to object type, such as
static_cast<char&&>(x)
; - any expression that designates a temporary object, after temporary materialization. (since C++17)
Properties:
- Same as rvalue (below).
- Same as glvalue (below).
In particular, like all rvalues, xvalues bind to rvalue references, and like all glvalues, xvalues may be polymorphic, and non-class xvalues may be cv-qualified.
Mixed categories
glvalue
A glvalue expression is either lvalue or xvalue.
Properties:
- A glvalue may be implicitly converted to a prvalue with lvalue-to-rvalue, array-to-pointer, or function-to-pointer implicit conversion.
- A glvalue may be polymorphic: the dynamic type of the object it identifies is not necessarily the static type of the expression.
- A glvalue can have incomplete type, where permitted by the expression.
rvalue
An rvalue expression is either prvalue or xvalue.
Properties:
-
Address of an rvalue may not be taken:
&int()
,&i++
[3],&42
, and&std::move(x)
are invalid. -
An rvalue can't be used as the left-hand operand of the built-in assignment or compound assignment operators.
-
An rvalue may be used to initialize a const lvalue reference, in which case the lifetime of the object identified by the rvalue is extended until the scope of the reference ends.
-
An rvalue may be used to initialize an rvalue reference, in which case the lifetime of the object identified by the rvalue is extended until the scope of the reference ends.When used as a function argument and when two overloads of the function are available, one taking rvalue reference parameter and the other taking lvalue reference to const parameter, an rvalue binds to the rvalue reference overload (thus, if both copy and move constructors are available, an rvalue argument invokes the move constructor, and likewise with copy and move assignment operators). (since C++11)
NOTE: 参见
C++\Language-reference\Basic-concept\Object\Lifetime-and-storage-duration\Lifetime
。
Special categories
Pending member function call
The expressions a.mf and p->mf, where mf
is a non-static member function, and the expressions a.*pmf and p->*pmf, where pmf
is a pointer to member function, are classified as prvalue expressions, but they cannot be used to initialize references, as function arguments, or for any purpose at all, except as the left-hand argument of the function call operator, e.g. (p->*pmf)(args).
Void expressions
Function call expressions returning void, cast expressions to void, and throw-expressions are classified as prvalue expressions, but they cannot be used to initialize references or as function arguments. They can be used in discarded-value contexts (e.g. on a line of its own, as the left-hand operand of the comma operator, etc.) and in the returnstatement in a function returning void. In addition, throw-expressions may be used as the second and the third operands of the conditional operator ?:.
Void expressions have no result object.(since C++17)
Bit fields
An expression that designates a bit field (e.g. a.m, where a
is an lvalue of type struct A { int m: 3; }) is an lvalue expression: it may be used as the left-hand operand of the assignment operator, but its address cannot be taken and a non-const lvalue reference cannot be bound to it. A const lvalue reference can be initialized from a bit-field lvalue, but a temporary copy of the bit-field will be made: it won't bind to the bit field directly.