cppreference.com关于值类型的详细解读:lvalue,rvalue,xvalue,prvalue,glvalue

注意:为了防止混淆,一般来说,本文将value categories翻译为值类型,type翻译为型别。


值类型(value categories)


每一个C++表达式(带有运算对象[operand]的运算符、字面值[literal]、变量名等)都是由两个独立的属性(properties) ———— 型别[type]和值类型[value categories] ———— 来描述[characterized]的。每一个表达式都有某些非引用类型[non-reference type],并且每一个表达式都明确地属于下面三种基本值类型之一。


基本类型(primary categories)

基本值类型与表达式的两个属性相对应:
1) 有"身份"[has identity]:能够确定某个表达式是否和另一个表达式指涉[refers to]同一个实体,例如,通过比较它们标识[identify]出来的函数或者对象的地址(直接或间接得到的)。
2) 能被移动[can be moved from]:能够被移动构造函数、移动赋值操作符或者其它实现[implement]移动语义[move semantics]的重载函数绑定[bind to]。

根据上面两个属性,我们可以对表达式进行分类:
1) 有"身份"但是不能"被移动"的表达式被称为左值表达式[lvalue expression];
2) 有"身份"同时能"被移动"的表达式被称为x值表达式[xvalue expression];
3) 没有"身份"但是能"被移动"的表达式被称为纯右值表达式[prvalue expression];
4) C++没有既没有"身份"也不能"被移动"的表达式;


lvalue 

左值[lvalue,left value]表达式是有"身份"但是不能"被移动"的表达式。这个名字的由来是有历史原因的,它反映了在CPL编程语言中左值是作为赋值运算符的左运算数[left-hand operand]的。

下面的表达式都是左值表达式:
1) 在作用域(scope)内的、无论什么类型的变量名或者函数名。比如,std::cin或者std::endl。即使这个变量的类型是右值引用,由它的名字组成的表达式也是个左值表达式。
2) 把左值引用作为返回的函数或者重载操作符。 比如,std::getline(std::cin,str),std::cout << 1, str1 = str2, 或 ++it;
3) a = b, a += b, a %= b, 以及其他所有内置[built-in]的赋值或者复合赋值[compound assignment]表达式。
4) ++a和--a,内置的前置自增[pre-increment]和前置自减[pre-decrement]表达式。
5) *p, 内置的指针取值[indirection]表达式。
6) a[n] 和 p[n], 内置的下标[subscript]索引表达式,【除非[except] ‘a’ 是一个右值数组。(since C++11)】
7) a.m, 对象的取成员变量表达式,除非[except] ‘m’ 是枚举成员或者非静态成员函数,或者 ‘a’ 是右值,以及 ‘m’ 是非引用类型的非静态数据成员。
8) p->m, “对象指针调取内置类型的成员变量” 的表达式,除非[except] ‘m’ 是枚举成员或者非静态成员函数。
9) a.*mp, 对象的成员指针表达式[the pointer to member of object expression],其中,‘a’必须是左值,‘mp’ 是指向数据成员的指针。
10) a->*mp, 对象指针的成员指针表达式,要求:‘mp’指向的是‘a’的数据成员。
11) a,b, 内置的逗号表达式,要求b必须为左值。 
12) a ? b : c, 对于某些a,b,c的三元条件表达式[ternary conditional expression]。
13) 字符串的字面值类型。如: “Hello World”。
14) 强转为左值引用类型的表达式,如:static_cast(x);
15) 把右值引用作为返回的函数或者重载操作符。[a function call or an overload operator expression of rvalue reference to function return type]
16) 强转为函数的右值引用类型的表达式,如:static_cast(x);
【注:15) 16) 都是从C++11开始使用的】

左值表达式的属性[properties]:
1) 拥有glvalue[generalized left value, 广义左值]表达式的所有属性。
2) 可以得到一个左值表达式的地址,例如:&++i和&std::endl是合法的表达式。(假设i是内置类型或者重载的前置自增操作符的返回值是左值引用)
3) 可修改的[modifiable]的左值可以被用作内置的赋值或者复合赋值操作符的左操作数[left-hand operand].
4) 左值可以被用来初始化左值引用。这个操作可以将一个确定的对象关联到一个新的名字。


rvalue【until C++11】 prvalue【since C++11】

纯右值[prvalue, pure right value]表达式是没有“身份”,能够“被移动”的表达式。
下面的表达式都是纯右值表达式:
1) 除字符串类型外的字面值[literal],如:42, true, nullptr。
2) 把非引用类型作为返回的函数或者重载操作符。如, str.substr(1,2), str1 + str2, it++。
3) a++ 和 a--,内置的后置自增[post-increment]和后置递减[post-decrement]操作符表达式。
4) a+b, a%b, a&b, a< 5) a&&b, a||b, ~a,内置的逻辑表达式。
6) a=b,以及所有其他内置的比较表达式。
7) &a, 内置的取址[address-of]表达式。
8) a.m,  对象的取成员变量表达式。其中,‘m’是枚举成员或非静态成员函数,【或者‘a’ 是右值,以及 ‘m’ 是非引用类型的非静态数据成员。(until C++11)】。
9) p->m, 内置的指针取值表达式,其中,m是枚举成员或者非静态成员函数。
10) a.*mp, 对象的成员指针表达式[the pointer to member of object expression]。 其中,‘mp’是指向‘a’的成员函数的指针,【或者,‘a’是一个右值,‘mp’是指向数据成员的指针。(until C++11)】。
11) a->*mp, 对象指针的成员指针表达式,其中, ‘mp’指向的是‘a’的成员函数。 
12) a,b, 内置的逗号表达式,其中,b必须为右值。
13) a?b:c, a ? b : c, 对于某些a,b,c的三元条件表达式[ternary conditional expression]。
14) 强转为非引用类型的表达式,比如,static_cast(x), std::string{}, (int)42 。
15) 【lambda 表达式,比如,[](int x){ return x*x; } (since C++11)】。

纯右值表达式的属性[properties]:
1) 拥有右值[rvalue]表达式的所有属性。
2) 纯右值表达式不能是多态的[polymorphic]:纯右值表达式标识对象的动态型别总是表达式的型别。[the dynamic type of the object it identifies is always the type of the expression. ]
3) 非类[non-class]的纯右值不能被const或volatile关键字标识[cv-qualified]。
4) 纯右值不能有不完整的型别[type]。除非[except]它是void类型或者用于decltype说明符[specifier]时。


xvalue【since C++11】

x值[xvalue, expiring value]表达式是既有“身份”[has identify],也能够“被移动”[can be moved from]的表达式。
下面的表达式都是x值表达式:
1) 把右值引用类型作为返回的函数或者重载操作符。例如, std::move(x);
2) a[n], 内置的下标[subscript]表达式,其中,‘a’是一个右值数组。
3) a.m,  对象的取成员变量表达式。其中,‘a’是一个右值,‘m’ 是非引用类型的非静态数据成员。
4)a.*mp, 对象的成员指针表达式[the pointer to member of object expression]。其中,‘a’是右值,‘mp’是指向数据成员的指针。
5) a?b:c, a ? b : c, 对于某些a,b,c的三元条件表达式[ternary conditional expression]。
6) 强转为“对象的右值引用”表达式,比如,static_cast(x)。

x值表达式的属性[properties]:
1) 拥有右值[rvalue]表达式的所有属性。
2) 拥有左值[glvalue]表达式的所有属性。
[注] 类似于纯右值,x值绑定右值引用,但不同的是,x值可能是多态的[polymorphic],并且非类[non-class]的x值可能被const或volatile关键字标识[cv-qualified]。


混合值类型(maxed gategories)

glvalue

广义左值[泛左值, glvalue, generalized left value]表达式是一个左值表达式或者x值表达式。广义表达式“有身份”,它可能能够“被移动”,也可能不能“被移动”。
广义左值表达式的属性[properties],这些属性同样适用于C++11以前的左值:
1) 广义左值可能被隐式地[implicitly]转换为纯右值。这是因为有左值到右值,数组到指针,函数到指针的隐式转换。[a glvalue may be implicitly converted to a prvalue with lvalue-to-rvalue, array-to-pointer, or functon-to-pointer implicit conversion.]
2) 广义左值表达式可能是多态的[polymorphic]:表达式标识对象的动态型别不必是总是表达式的静态型别。
3) 只要表达式合法,广义左值能够有不完整类型。


rvalue

右值[rvalue, right value]表达式是一个纯右值表达式或x值表达式。右值表达式可能“有身份”,也可能没“有身份”,但能够“被移动”。这个名字的由来是有历史原因的,它反映了在CPL编程语言中左值是作为赋值运算符的右运算数[right-hand operand]的。

右值值表达式的属性[properties],这些属性同样适用于C++11以前的右值:
1) 右值不能[may not]被取地址。例如,&int(),  &i++, &std::move(x)都是不合法的。
2) 右值不能[can not]被用作内置的赋值或复合赋值表达式的左操作数[left-hand operand]。
3) 右值可能被用来初始化常左值引用[const lvalue reference],在这种情况下,这个右值标识对象[the object identified by the rvalue]的生命周期[lifetime]会被延长到这个引用的作用域[scope]的结束。
4) 右值可能被用来初始化常右值引用,在这种情况下,这个右值标识对象[the object identified by the rvalue]的生命周期[lifetime]会被延长到这个引用的作用域[scope]的结束。
5) 作为函数参数[argument],如果有两个重载函数可用[avaliable],其中一个把右值引用作为参数[parameter],另一个把常左值引用作为参数,那么右值会绑定[bind to]那个把右值引用作为参数的重载函数。(因此,在拷贝构造函数和移动构造函数都可用[available]的情况下,右值参数会调用[invoke]移动构造函数,拷贝赋值符号和移动赋值符号与之类似。)
【注:4)、5)从C++11开始使用】


特殊的值类型(special categories)


未定成员函数调用[pending member function call]
表达式a.mf和p->mf,其中,mf是非静态成员函数、表达式a.*mfp和p->*mfp,其中,mfp是指向成员函数的指针,都被分类[classified]为纯右值表达式,但是它们不能被用来初始化引用,或作为函数参数,除非[except]作为函数调用操作符的左参数[as the left-hand argument of the function call operator]。

空表达式(void expression)
函数调用表达式返回void、强转[cast]为void的表达式、以及throw表达式,都被分类为纯右值表达式,但是它们不能被用来初始化引用,或作为函数参数。它们能在某些情况[contexts]下使用,(比如,作为逗号操作符的左操作数等)作为函数返回void的返回声明[statement]。另外,throw表达式可以作为三元条件式?:的第二个或第三个操作数。

位域(bit field)
指定[designate]位域的表达式是一个左值表达式。(例如,struct A { int m : 3; }; ) 它可能用作赋值操作符的左操作数,但是它的地址不能被获取,它不能被非常左值引用绑定。常左值引用能被位域左值初始化,但是它并不是直接绑定位域,而是绑定一个临时的位域副本。


英文文献来源:cppreference.com

你可能感兴趣的:(C++)