一、简要说明
左值和右值在前面讲过几次,基本上大家对左右值有了一个初步的印象,但是具体到一些细节,可能还是分析的有些模糊,所以需要结合具体的标准文档把这左值和右值再进一步讲解一下。如果单纯是在c++11标准以前,讲这个左值右值的意义其实并没有多大,但随着c++11标准及以上标准不断的推出,右值的应用越来越广泛。因而,讲明白右值和左值的一些区别,应用上的一些不同,还是大有必要的。
二、左值和右值
在新的c++标准中,有rvalue和glvalue,也就是在前面提到过的右值和泛左值。在旧的c++标准(c++11以前)右值就是普通认知里的右值,但在在新的c++标准中,传统意义上右值包含prvalue纯右值和xvalue(消亡值,即通过右值引用产生的值);而泛左值这个概念也需要说明一下,它相当于传统的lvalue和xvalue。弄明白这个定义后,再学习一些基本的文档资料时才不会犯晕。 1、左值 The following expressions are lvalue expressions: 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; (变量或者函数是左值,c++20中的模板参数) 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. (左值可用于初始化左值引用;这会将新名称与表达式标识的对象相关联。)
概括一下:有名变量和可取地址的变量是左值。需要注意的是右值引用是左值。不能把等号左边看成左值,原因是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指针也是纯右值,上面说过,地址是纯右值) an enumerator; non-type template parameter unless its type is a class or (since C++20) an lvalue reference type; a lambda expression, such as [](int x){ return x * x; };(since C++11) (lambda表达式是纯右值) a requires-expression, such as requires (T i) { typename T::type; }; a specialization of a concept, such as std::equality_comparable.(since c++20) 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. unless it is materialized in order to be bound to a reference to a cv-qualified type (since C++17). (Note: a function call or cast expression may result in a prvalue of non-class cv-qualified type, but the cv-qualifier is generally immediately stripped out.) (不是class也不是数组的纯右值不能声明为cv限定符(const, volatile, const-volatile)) 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. (纯右值不能为抽象类类型和由其组成的数组)
传统的右值中包含这种纯右值即表达式的中间状态值。正如前边说的,这玩意儿没法取地址。
3、消亡值 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). 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. (消亡连接右值表达式,可以是动态类型,但是非class的类型不能是cv-qualified的)
4、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. (glvalue可以隐式转换为prvalue,并进行左值到右值、数组到指针或函数到指针的隐式转换) A glvalue may be polymorphic: the dynamic type of the object it identifies is not necessarily the static type of the expression. (glvalue可能是多态的:它标识的对象的动态类型不一定是表达式的静态类型。) 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 cannot be taken by built-in address-of operator: &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. (rvalue可用于初始化const-lvalue引用,在这种情况下,由rvalue标识的对象的生存期将延长,直到引用的范围结束。) 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) (当用作函数参数并且函数的两个重载可用时,一个采用右值引用参数,另一个采用左值引用 const 参数,右值绑定到右值引用重载(因此,如果复制和移动构造函数都可用,右值参数调用移动构造函数,同样使用复制和移动赋值运算符))
5、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 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, 将表达式转换为void, 和throw 表达式被归类为纯右值表达式,但它们不能用于初始化引用或作为函数参数。它们可以用在丢弃值的上下文中(例如,在它自己的一行中,作为逗号运算符的左侧操作数等)和返回 函数中的语句返回void, 此外,抛出表达式可以用作条件运算符 ?:的第二个和第三个操作数。)
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 a glvalue 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 or rvalue reference can be initialized from a bit-field glvalue, but a temporary copy of the bit-field will be made: it won’t bind to the bit field directly. (指定位域的表达式(例如一种。米, 其中a是类型的左值struct A { int m : 3 ; }) 是一个泛左值表达式:它可以用作赋值运算符的左手操作数,但它的地址不能被获取并且非const 左值引用不能绑定到它。const 左值引用或右值引用可以从位域泛左值初始化,但会生成位域的临时副本:它不会直接绑定到位域。)
三、总结
以上的主要内容,其实是翻译自标准文档说明,在网站:https://en.cppreference.com/w/cpp/language/value_category上可以查看。以标准为标准,但要小心做编译器的厂商挟带私货。
|