General
每一個C++表達式(一個操作符和它的操作數,一個字面值,一個變量名等等)都代表着兩個獨立屬性:類型+屬性分類。在現代C++中
glvalue(泛左值) = lvalue (傳統意義上的左值)+ xvalue(消亡值,通過右值引用產生) rvalue (傳統意義上的右值) = prvalue(純右值) + xvalue
Primary categories
1. lvalue(左值)
The following expressions are lvalue expressions:
- the name of a variable or a function in scope, 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;
(變量,函數都是左值) - a function call or an overloaded operator expression of lvalue reference return type, such as std::getline(std::cin, str), std::cout << 1, str1 = str2, or ++it;
(運算符重載並且返回值是引用的方法) - a = b, a += b, a %= b, and all other built-in assignment and compound assignment expressions;
(內建類型和組合類型是左值) - ++a and --a, the built-in pre-increment and pre-decrement expressions;
(前自增是左值,可以取地址,即&++a) - *p, the built-in indirection expression;
(內建指針類型) - a[n] and p[n], the built-in subscript expressions, except where a is an array rvalue (since C++11);
(內建下標運算符,(在C++11,除了數組類型為右值數組)) - 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; (內建.運算符,a是左值,當m是非靜態成員函數或者數據時,表達式為左值)
- p->m, the built-in member of pointer expression, except where m is a member enumerator or a non-static member function; (內建->運算符,除了m是枚舉或者非靜態成員函數,否則都是左值)
- 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;
(指向成員的函數指針,只要是指向成員為數據,則表達式為左值) - a, b, the built-in comma expression, where b is an lvalue;
(內建逗號表達式,此時逗號表達式最右的值是左值) - a ? b : c, the ternary conditional expression for some a, b, and c;
- a string literal, such as "Hello, world!";
(特別注意,C++規定字符串是左值) - a cast expression to lvalue reference type, such as static_cast<int&>(x);
- a function call or an overloaded operator expression of rvalue reference to function return type;
(C++ 11 類里面的指定左值返回類型的成員函數(即 class{ void fun()&; },fun是左值)) - a cast expression to rvalue reference to function type, such as static_cast<void (&&)(int)>(x).
(C++ 11 右值強轉函數類型)
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.
總結:
簡單的來說,能取地址的變量一定是左值,有名字的變量也一定是左值,最經典的void fun(p&& shit),其中shit也是左值,因為右值引用是左值(所以才會有move,forward這些函數的產生,其中move出來一定是右值,forward保持變量形式和之前的不變,就是為了解決右值引用是左值的問題)。
至於為什么不能把等號左邊看成左值,因為在C++中,等號是可以運算符重載的,等號完全可以重載成為等號左邊為右值的形式。
2. prvalue(純右值)
The following expressions are prvalue expressions:
- a literal (except for string literal), such as 42, true or nullptr;
(字面值常量,除了字符串,都是純右值,包括空指針,true和false) - a function call or an overloaded operator expression of non-reference return type, such as str.substr(1, 2), str1 + str2, or it++;
(返回值是非引用的表達式是純右值) - a++ and a--, the built-in post-increment and post-decrement expressions;
(后自增表達式是純右值) - a + b, a % b, a & b, a << b, and all other built-in arithmetic expressions;
- a && b, a || b, ~a, the built-in logical expressions;
- a < b, a == b, a >= b, and all other built-in comparison expressions;
- &a, the built-in address-of 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); (內建.運算符,m可以是枚舉或者成員函數,在C++11還可以是純右值)
- p->m, the built-in member of pointer expression, where m is a member enumerator or a non-static member function[2]; (內建->運算符,m可以是枚舉或者成員函數)
- a.*mp, the pointer to member of object expression, where mp is a pointer to member function[2], or where a is an rvalue and mp is a pointer to data member (until C++11);
(內建.*運算符m可以是枚舉或者成員函數,在C++11還可以是純右值) - p->*mp, the built-in pointer to member of pointer expression, where mp is a pointer to member function[2];
(內建->*運算符,m可以是枚舉或者成員函數) - a, b, the built-in comma expression, where b is an rvalue;
- a ? b : c, the ternary conditional expression for some a, b, and c;
- a cast expression to non-reference type, such as static_cast(x), std::string{}, or (int)42;
- the this pointer;
(this指針也是純右值,因為this也是一個地址) - a lambda expression, such as [](int x){ return x * x; }.(since C++11)
(lambda表達式是純右值)
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.
(不是class也不是數組的純右值不能聲明為const, volatile, const-volatile) - A prvalue cannot have incomplete type (except for type void, see below, or when used in decltype specifier).
(純右值不能是不完整類型)
總結:
純右值是傳統右值的一部分,純右值是表達式產生的中間值,不能取地址。
3. xvalue(消亡值)
The following expressions are xvalue expressions:
- a function call or an overloaded operator expression of rvalue reference to object return type, 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, where a is an rvalue and m is a non-static data member of non-reference type;
- a.*mp, the pointer to member of object expression, where a is an rvalue and mp is a pointer to data member;
- a ? b : c, the ternary conditional expression for some a, b, and c;
- a cast expression to rvalue reference to object type, such as static_cast<char&&>(x).
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.
(消亡連接右值表達式,可以是動態類型,但是非class的類型不能是cv-qualified的)
總結:
本質上,消亡值就是通過右值引用產生的值。右值一定會在表達式結束后被銷毀,比如return x(x被copy以后會被銷毀), 1+2(3這個中間值會被銷毀)。
Mixed categories
1. 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.
2. 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.(since C++11)
- 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)
(在C++11中,如果存在一個函數的兩個重載(只有某個參數不同,且這個參數在一個重載中是右值引用,另一個是const左值引用),此時用右值調用這個重載函數,將會匹配右值引用的函數)
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.mfp and p->mfp, where mfp 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->*mfp)(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 return statement 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.
(位域是左值,但是位域不能取地址,也不能將一個非const引用綁定到位域上,但是const引用可以綁定到一個位域上,但此時會產生一個臨時變量)
Footnotes
-
Assuming i has built-in type or the pre-increment operator is overloaded to return by lvalue reference
2 2.0 2.1 2.2 2.3 special rvalue category, see pending member function call
-
Assuming i has built-in type or the postincrement operator is not overloaded to return by lvalue reference
-
"A difference of opinion within the C community centered around the meaning of lvalue, one group considering an lvalue to be any kind of object locator, another group holding that an lvalue is meaningful on the left side of an assigning operator. The C89 Committee adopted the definition of lvalue as an object locator" -- ANSI C Rationale, 6.3.2.1/10
-
"New" Value Terminology by Bjarne Stroustrup, 2010
-
const prvalues (only allowed for class types) and const xvalues do not bind to T&& overloads, but they bind to the const T&& overloads, which are also classified as "move constructor" and "move assignment operator" by the standard, satisfying the definition of "can be moved from" for the purpose of this classification. However, such overloads cannot modify their arguments and are not used in practice; in their absence const prvalues and const xvalues bind to const T& overloads.
翻譯自:http://en.cppreference.com/w/cpp/language/value_category#cite_ref-1有刪改