C++ 什么是右值、左值、xvalues、glvalues 和 prvalues?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3601602/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 13:15:33  来源:igfitidea点击:

What are rvalues, lvalues, xvalues, glvalues, and prvalues?

c++expressionc++-faqc++11

提问by James McNellis

In C++03, an expression is either an rvalueor an lvalue.

在 C++03 中,表达式是rvaluelvalue

In C++11, an expression can be an:

在 C++11 中,表达式可以是:

  1. rvalue
  2. lvalue
  3. xvalue
  4. glvalue
  5. prvalue
  1. 右值
  2. 左值
  3. 格列卫
  4. 右值

Two categories have become five categories.

两个类别变成了五个类别。

  • What are these new categories of expressions?
  • How do these new categories relate to the existing rvalue and lvalue categories?
  • Are the rvalue and lvalue categories in C++0x the same as they are in C++03?
  • Why are these new categories needed? Are the WG21gods just trying to confuse us mere mortals?
  • 这些新的表达类别是什么?
  • 这些新类别与现有的右值和左值类别有何关联?
  • C++0x 中的右值和左值类别是否与 C++03 中的相同?
  • 为什么需要这些新类别?是WG21神只是想迷惑我们凡人?

回答by Kornel Kisielewicz

I guess this document might serve as a not so short introduction : n3055

我想这个文档可以作为一个不那么简短的介绍:n3055

The whole massacre began with the move semantics. Once we have expressions that can be moved and not copied, suddenly easy to grasp rules demanded distinction between expressions that can be moved, and in which direction.

整个大屠杀开始于移动语义。一旦我们有了可以移动不可复制的表达式,突然容易掌握的规则要求区分可以移动的表达式,以及向哪个方向移动。

From what I guess based on the draft, the r/l value distinction stays the same, only in the context of moving things get messy.

根据我根据草案的猜测,r/l 值区别保持不变,仅在移动事物变得混乱的情况下。

Are they needed? Probably not if we wish to forfeit the new features. But to allow better optimization we should probably embrace them.

他们需要吗?如果我们希望放弃新功能,可能不会。但是为了允许更好的优化,我们可能应该接受它们。

Quoting n3055:

引用n3055

  • An lvalue(so-called, historically, because lvalues could appear on the left-hand side of an assignment expression) designates a function or an object. [Example: If Eis an expression of pointer type, then *Eis an lvalue expression referring to the object or function to which Epoints. As another example, the result of calling a function whose return type is an lvalue reference is an lvalue.]
  • An xvalue(an “eXpiring” value) also refers to an object, usually near the end of its lifetime (so that its resources may be moved, for example). An xvalue is the result of certain kinds of expressions involving rvalue references. [Example: The result of calling a function whose return type is an rvalue reference is an xvalue.]
  • A glvalue(“generalized” lvalue) is an lvalueor an xvalue.
  • An rvalue(so-called, historically, because rvalues could appear on the right-hand side of an assignment expression) is an xvalue, a temporary object or subobject thereof, or a value that is not associated with an object.
  • A prvalue(“pure” rvalue) is an rvalue that is not an xvalue. [Example: The result of calling a function whose return type is not a reference is a prvalue]
  • 一个左值(所谓的,历史上,因为左值可以出现在赋值表达式的左侧)表示一函数或一个对象。 [示例:如果E是指针类型的表达式,则*E是指指向所指向的对象或函数的左值表达式E。再举一个例子,调用一个返回类型为左值引用的函数的结果是一个左值。]
  • 一个x值(一个“到期”值)也指一个对象,通常接近其寿命的末尾(使得其资源可以被移动,例如)。xvalue 是某些类型的涉及右值引用的表达式的结果。 [示例:调用返回类型为右值引用的函数的结果是 xvalue。]
  • glvalue(“广义”左值)是左值x值
  • 右值(所谓的,历史上,因为右值可以出现在赋值表达式的右手侧)是x值,临时对象或子对象,或它们的不与一个对象相关联的值。
  • prvalue(“纯”右值)是一个rvalue这不是一个x值。 [示例:调用返回类型不是引用的函数的结果是纯右值]

The document in question is a great reference for this question, because it shows the exact changes in the standard that have happened as a result of the introduction of the new nomenclature.

有问题的文件是这个问题的重要参考,因为它显示了由于引入新命名法而发生的标准的确切变化。

回答by dirkgently

What are these new categories of expressions?

这些新的表达类别是什么?

The FCD (n3092)has an excellent description:

FCD(n3092)具有优异的描述:

— An lvalue (so called, historically, because lvalues could appear on the left-hand side of an assignment expression) designates a function or an object. [ Example: If E is an expression of pointer type, then *E is an lvalue expression referring to the object or function to which E points. As another example, the result of calling a function whose return type is an lvalue reference is an lvalue. —end example ]

— An xvalue (an “eXpiring” value) also refers to an object, usually near the end of its lifetime (so that its resources may be moved, for example). An xvalue is the result of certain kinds of expressions involving rvalue references (8.3.2). [ Example: The result of calling a function whose return type is an rvalue reference is an xvalue. —end example ]

— A glvalue (“generalized” lvalue) is an lvalue or an xvalue.

— An rvalue (so called, historically, because rvalues could appear on the right-hand side of an assignment expressions) is an xvalue, a temporary object (12.2) or subobject thereof, or a value that is not associated with an object.

— A prvalue (“pure” rvalue) is an rvalue that is not an xvalue. [ Example: The result of calling a function whose return type is not a reference is a prvalue. The value of a literal such as 12, 7.3e5, or true is also a prvalue. —end example ]

Every expression belongs to exactly one of the fundamental classifications in this taxonomy: lvalue, xvalue, or prvalue. This property of an expression is called its value category. [ Note: The discussion of each built-in operator in Clause 5 indicates the category of the value it yields and the value categories of the operands it expects. For example, the built-in assignment operators expect that the left operand is an lvalue and that the right operand is a prvalue and yield an lvalue as the result. User-defined operators are functions, and the categories of values they expect and yield are determined by their parameter and return types. —end note

— 左值(历史上如此称呼,因为左值可以出现在赋值表达式的左侧)指定一个函数或一个对象。[ 示例:如果 E 是指针类型的表达式,则 *E 是一个左值表达式,指的是 E 指向的对象或函数。再举一个例子,调用一个返回类型为左值引用的函数的结果是一个左值。—结束示例]

— xvalue(一个“eXpiring”值)也指一个对象,通常接近其生命周期的终点(例如,它的资源可能会被移动)。xvalue 是某些类型的涉及右值引用的表达式的结果 (8.3.2)。[ 示例:调用返回类型为右值引用的函数的结果是 xvalue。—结束示例]

— 左值(“广义”左值)是左值或 x 值。

— 右值(历史上如此称呼,因为右值可能出现在赋值表达式的右侧)是 xvalue、临时对象 (12.2) 或其子对象,或与对象无关的值。

— 纯右值(“纯”右值)是不是 xvalue 的右值。[ 示例:调用返回类型不是引用的函数的结果是纯右值。诸如 12、7.3e5 或 true 之类的文字值也是纯右值。—结束示例]

每个表达式都完全属于此分类法中的基本分类之一:lvalue、xvalue 或 prvalue。表达式的此属性称为其值类别。[ 注意:第 5 条中对每个内置运算符的讨论表明它产生的值的类别和它期望的操作数的值类别。例如,内置赋值运算符期望左操作数是左值,右操作数是右值,并产生左值作为结果。用户定义的运算符是函数,它们期望和产生的值的类别由它们的参数和返回类型决定。——尾注

I suggest you read the entire section 3.10 Lvalues and rvaluesthough.

不过,我建议您阅读整个部分3.10 Lvalues and rvalues

How do these new categories relate to the existing rvalue and lvalue categories?

这些新类别与现有的右值和左值类别有何关联?

Again:

再次:

Taxonomy

分类

Are the rvalue and lvalue categories in C++0x the same as they are in C++03?

C++0x 中的右值和左值类别是否与 C++03 中的相同?

The semantics of rvalues has evolved particularly with the introduction of move semantics.

右值的语义特别随着移动语义的引入而发展。

Why are these new categories needed?

为什么需要这些新类别?

So that move construction/assignment could be defined and supported.

这样就可以定义和支持移动构造/分配。

回答by sellibitze

I'll start with your last question:

我将从你的最后一个问题开始:

Why are these new categories needed?

为什么需要这些新类别?

The C++ standard contains many rules that deal with the value category of an expression. Some rules make a distinction between lvalue and rvalue. For example, when it comes to overload resolution. Other rules make a distinction between glvalue and prvalue. For example, you can have a glvalue with an incomplete or abstract type but there is no prvalue with an incomplete or abstract type. Before we had this terminology the rules that actually need to distinguish between glvalue/prvalue referred to lvalue/rvalue and they were either unintentionally wrong or contained lots of explaining and exceptions to the rule a la "...unless the rvalue is due to unnamed rvalue reference...". So, it seems like a good idea to just give the concepts of glvalues and prvalues their own name.

C++ 标准包含许多处理表达式的值类别的规则。一些规则区分左值和右值。例如,当涉及到重载解析时。其他规则对左值和右值进行了区分。例如,您可以有一个不完整或抽象类型的左值,但没有不完整或抽象类型的纯右值。在我们使用这个术语之前,实际上需要区分左值/右值的规则是指左值/右值,它们要么是无意中错误的,要么是包含大量规则的解释和例外,“...除非右值是由于未命名的右值引用...”。因此,给左值和右值的概念命名似乎是个好主意。

What are these new categories of expressions? How do these new categories relate to the existing rvalue and lvalue categories?

这些新的表达类别是什么?这些新类别与现有的右值和左值类别有何关联?

We still have the terms lvalue and rvalue that are compatible with C++98. We just divided the rvalues into two subgroups, xvalues and prvalues, and we refer to lvalues and xvalues as glvalues. Xvalues are a new kind of value category for unnamed rvalue references. Every expression is one of these three: lvalue, xvalue, prvalue. A Venn diagram would look like this:

我们仍然有与 C++98 兼容的术语 lvalue 和 rvalue。我们只是将右值分为两个子组,xvalues 和 prvalues,我们将 lvalues 和 xvalues 称为左值。Xvalues 是一种用于未命名右值引用的新值类别。每个表达式都是以下三个之一:lvalue、xvalue、prvalue。维恩图如下所示:

    ______ ______
   /      X      \
  /      / \      \
 |   l  | x |  pr  |
  \      \ /      /
   \______X______/
       gl    r

Examples with functions:

函数示例:

int   prvalue();
int&  lvalue();
int&& xvalue();

But also don't forget that named rvalue references are lvalues:

但也不要忘记命名的右值引用是左值:

void foo(int&& t) {
  // t is initialized with an rvalue expression
  // but is actually an lvalue expression itself
}

回答by Nicol Bolas

Why are these new categories needed? Are the WG21 gods just trying to confuse us mere mortals?

为什么需要这些新类别?WG21 的众神只是想迷惑我们这些凡人吗?

I don't feel that the other answers (good though many of them are) really capture the answer to this particular question. Yes, these categories and such exist to allow move semantics, but the complexity exists for one reason. This is the one inviolate rule of moving stuff in C++11:

我不认为其他答案(虽然很多都很好)真正抓住了这个特定问题的答案。是的,这些类别的存在是为了允许移动语义,但存在复杂性的原因之一。这是在 C++11 中移动东西的一个不可侵犯的规则:

Thou shalt move only when it is unquestionably safe to do so.

只有在绝对安全的情况下,您才能移动。

That is why these categories exist: to be able to talk about values where it is safe to move from them, and to talk about values where it is not.

这就是存在这些类别的原因:能够谈论可以安全转移的价值,并谈论不安全的价值。

In the earliest version of r-value references, movement happened easily. Tooeasily. Easily enough that there was a lot of potential for implicitly moving things when the user didn't really mean to.

在 r 值引用的最早版本中,移动很容易发生。容易了。很容易,当用户并不是真的想隐式移动东西时,它有很大的潜力。

Here are the circumstances under which it is safe to move something:

以下是可以安全移动物品的情况:

  1. When it's a temporary or subobject thereof. (prvalue)
  2. When the user has explicitly said to move it.
  1. 当它是临时对象或其子对象时。(右值)
  2. 当用户明确表示要移动它时

If you do this:

如果你这样做:

SomeType &&Func() { ... }

SomeType &&val = Func();
SomeType otherVal{val};

What does this do? In older versions of the spec, before the 5 values came in, this would provoke a move. Of course it does. You passed an rvalue reference to the constructor, and thus it binds to the constructor that takes an rvalue reference. That's obvious.

这有什么作用?在旧版本的规范中,在 5 个值出现之前,这会引发移动。当然可以。您将右值引用传递给构造函数,因此它绑定到接受右值引用的构造函数。这很明显。

There's just one problem with this; you didn't askto move it. Oh, you might say that the &&should have been a clue, but that doesn't change the fact that it broke the rule. valisn't a temporary because temporaries don't have names. You may have extended the lifetime of the temporary, but that means it isn't temporary; it's just like any other stack variable.

这只有一个问题;你没有要求移动它。哦,你可能会说这&&应该是一个线索,但这并不能改变它违反规则的事实。val不是临时的,因为临时的没有名字。你可能延长了临时的生命周期,但这意味着它不是临时的;它就像任何其他堆栈变量一样。

If it's not a temporary, and you didn't ask to move it, then moving is wrong.

如果它不是临时的,并且您没有要求移动它,那么移动是错误的。

The obvious solution is to make valan lvalue. This means that you can't move from it. OK, fine; it's named, so its an lvalue.

显而易见的解决方案是创建val一个左值。这意味着您无法摆脱它。好的; 它是命名的,所以它是一个左值。

Once you do that, you can no longer say that SomeType&&means the same thing everwhere. You've now made a distinction between named rvalue references and unnamed rvalue references. Well, named rvalue references are lvalues; that was our solution above. So what do we call unnamed rvalue references (the return value from Funcabove)?

一旦你这样做了,你就不能再说这SomeType&&在任何地方都意味着同样的事情。您现在已经区分了命名的右值引用和未命名的右值引用。好吧,命名的右值引用是左值;那是我们上面的解决方案。那么我们怎么称呼未命名的右值引用(Func上面的返回值)呢?

It's not an lvalue, because you can't move from an lvalue. And we needto be able to move by returning a &&; how else could you explicitly say to move something? That is what std::movereturns, after all. It's not an rvalue (old-style), because it can be on the left side of an equation (things are actually a bit more complicated, see this questionand the comments below). It is neither an lvalue nor an rvalue; it's a new kind of thing.

它不是左值,因为你不能从左值移动。我们需要能够通过返回 a 来移动&&;你怎么能明确地说要移动一些东西?std::move毕竟,这就是回报。它不是右值(旧式),因为它可以位于等式的左侧(事情实际上有点复杂,请参阅此问题和下面的评论)。它既不是左值也不是右值;这是一种新事物。

What we have is a value that you can treat as an lvalue, exceptthat it is implicitly moveable from. We call it an xvalue.

我们拥有的是一个您可以将其视为左值的值,除了它可以隐式移动。我们称之为xvalue。

Note that xvalues are what makes us gain the other two categories of values:

请注意,xvalues 是使我们获得其他两类值的原因:

  • A prvalue is really just the new name for the previous type of rvalue, i.e. they're the rvalues that aren'txvalues.

  • Glvalues are the union of xvalues and lvalues in one group, because they do share a lot of properties in common.

  • 右值实际上只是前一种右值的新名称,即它们是不是xvalue的右值。

  • Glvalues 是一组中 xvalues 和 lvalues 的联合,因为它们确实共享许多共同的属性。

So really, it all comes down to xvalues and the need to restrict movement to exactly and only certain places. Those places are defined by the rvalue category; prvalues are the implicit moves, and xvalues are the explicit moves (std::movereturns an xvalue).

所以真的,这一切都归结为 xvalues 以及将运动限制在特定位置的需要。这些地方由右值类别定义;prvalues 是隐式移动,xvalues 是显式移动(std::move返回一个 xvalue)。

回答by Ivan Kush

IMHO, the best explanation about its meaning gave us Stroustrup+ take into account examples of Dániel Sándorand Mohan:

恕我直言,关于其含义的最佳解释给了我们Stroustrup+ 考虑Dániel SándorMohan 的例子:

Stroustrup:

斯特劳斯特鲁普:

Now I was seriously worried. Clearly we were headed for an impasse or a mess or both. I spent the lunchtime doing an analysis to see which of the properties (of values) were independent. There were only two independent properties:

  • has identity– i.e. and address, a pointer, the user can determine whether two copies are identical, etc.
  • can be moved from– i.e. we are allowed to leave to source of a "copy" in some indeterminate, but valid state

This led me to the conclusion that there are exactly three kinds of values (using the regex notational trick of using a capital letter to indicate a negative – I was in a hurry):

  • iM: has identity and cannot be moved from
  • im: has identity and can be moved from (e.g. the result of casting an lvalue to a rvalue reference)
  • Im: does not have identity and can be moved from.

    The fourth possibility, IM, (doesn't have identity and cannot be moved) is not useful in C++(or, I think) in any other language.

In addition to these three fundamental classifications of values, we have two obvious generalizations that correspond to the two independent properties:

  • i: has identity
  • m: can be moved from

This led me to put this diagram on the board: enter image description here

Naming

I observed that we had only limited freedom to name: The two points to the left (labeled iMand i) are what people with more or less formality have called lvaluesand the two points on the right (labeled mand Im) are what people with more or less formality have called rvalues. This must be reflected in our naming. That is, the left "leg" of the Wshould have names related to lvalueand the right "leg" of the Wshould have names related to rvalue.I note that this whole discussion/problem arise from the introduction of rvalue references and move semantics. These notions simply don't exist in Strachey's world consisting of just rvaluesand lvalues. Someone observed that the ideas that

  • Every valueis either an lvalueor an rvalue
  • An lvalueis not an rvalueand an rvalueis not an lvalue

are deeply embedded in our consciousness, very useful properties, and traces of this dichotomy can be found all over the draft standard. We all agreed that we ought to preserve those properties (and make them precise). This further constrained our naming choices. I observed that the standard library wording uses rvalueto mean m(the generalization), so that to preserve the expectation and text of the standard library the right-hand bottom point of the Wshould be named rvalue.

This led to a focused discussion of naming. First, we needed to decide on lvalue.Should lvaluemean iMor the generalization i? Led by Doug Gregor, we listed the places in the core language wording where the word lvaluewas qualified to mean the one or the other. A list was made and in most cases and in the most tricky/brittle text lvaluecurrently means iM. This is the classical meaning of lvalue because "in the old days" nothing was moved; moveis a novel notion in C++0x. Also, naming the topleft point of the Wlvaluegives us the property that every value is an lvalueor an rvalue, but not both.

So, the top left point of the Wis lvalueand the bottom right point is rvalue.What does that make the bottom left and top right points? The bottom left point is a generalization of the classical lvalue, allowing for move. So it is a generalized lvalue.We named it glvalue.You can quibble about the abbreviation, but (I think) not with the logic. We assumed that in serious use generalized lvaluewould somehow be abbreviated anyway, so we had better do it immediately (or risk confusion). The top right point of the W is less general than the bottom right (now, as ever, called rvalue). That point represent the original pure notion of an object you can move from because it cannot be referred to again (except by a destructor). I liked the phrase specialized rvaluein contrast to generalized lvaluebut pure rvalueabbreviated to prvaluewon out (and probably rightly so). So, the left leg of the W is lvalueand glvalueand the right leg is prvalueand rvalue.Incidentally, every value is either a glvalue or a prvalue, but not both.

This leaves the top middle of the W: im; that is, values that have identity and can be moved. We really don't have anything that guides us to a good name for those esoteric beasts. They are important to people working with the (draft) standard text, but are unlikely to become a household name. We didn't find any real constraints on the naming to guide us, so we picked ‘x' for the center, the unknown, the strange, the xpert only, or even x-rated.

Steve showing off the final product

现在我很担心。显然,我们正走向僵局或一团糟,或两者兼而有之。我花了午餐时间进行分析,看看哪些(值的)属性是独立的。只有两个独立的属性:

  • has identity– 即和地址,一个指针,用户可以判断两个副本是否相同等。
  • can be moved from– 即我们可以在某些不确定但有效的状态下保留“副本”的来源

这使我得出结论,正好有三种值(使用正则表达式符号技巧,使用大写字母表示否定——我很着急):

  • iM: 有身份,不能移动
  • im: 具有标识并且可以从中移动(例如将左值转换为右值引用的结果)
  • Im: 没有身份,可以移出。

    第四种可能性,IM, (没有身份且无法移动)在C++(或者,我认为)任何其他语言中都没有用。

除了这三个基本的值分类外,我们还有两个明显的概括对应于两个独立的属性:

  • i: 有身份
  • m: 可以从

这让我把这张图放在黑板上: 在此处输入图片说明

命名

我观察到我们命名的自由有限:左边的两个点(标记为iMi)是或多或少形式化的人所称的lvalues,右边的两个点(标记为mIm)是或多或少形式化的人所称的 有叫rvalues。这必须反映在我们的命名中。也就是说,the 的左“腿”W应该有相关的名称,lvalue而右“腿”W应该有相关的名称rvalue.我注意到这整个讨论/问题源于右值引用和移动语义的引入。这些概念在 Strachey 由 justrvalues和组成的世界中根本不存在lvalues。有人观察到,这些想法

  • 每个value都是一个lvalue或一个rvalue
  • 一个lvalue不是一个rvalue,一个rvalue不是一个lvalue

深深植根于我们的意识中,非常有用的特性,并且在标准草案中随处可见这种二分法的痕迹。我们都同意我们应该保留这些属性(并使它们精确)。这进一步限制了我们的命名选择。我观察到标准库的措辞使用rvalue来表示m(概括),以便保留标准库的期望和文本,W应该将右下角的点命名为 rvalue.

这导致了对命名的集中讨论。首先,我们需要决定lvalue.应该lvalue是平均iM还是泛化i?在 Doug Gregor 的带领下,我们列出了核心语言措辞中该词lvalue有资格表示一个或另一个的位置。列出了一个列表,在大多数情况下,在目前最棘手/脆弱的文本中,这 lvalue意味着iM. 这是左值的经典含义,因为“在过去”没有任何东西被移动;move是 中的一个新概念C++0x。此外,命名 the 的左上角Wlvalue为我们提供了每个值都是 anlvalue或 an的属性rvalue,但不是两者兼而有之。

那么,Wis 的左上角点lvalue和右下角点是rvalue.什么是左下角和右上角的点?左下点是经典左值的泛化,允许移动。所以它是一个generalized lvalue.我们命名它 glvalue.你可以对缩写提出质疑,但(我认为)不是逻辑。我们假设在严重使用中generalized lvalue无论如何都会被缩写,所以我们最好立即这样做(或冒着混淆的风险)。W 的右上角没有右下角那么一般(现在,一如既往,称为rvalue)。那个点代表一个你可以移动的对象的原始纯概念,因为它不能被再次引用(除非被析构函数引用)。specialized rvalue相比之下,我喜欢这句话,generalized lvalue但是pure rvalue缩写为prvalue赢了(可能是正确的)。因此,W的左腿lvalueglvalue和右腿prvaluervalue.顺便说一句,每个值可以是一个glvalue或prvalue,但不能同时使用。

这叶子的顶部中间Wim; 也就是说,具有标识且可以移动的值。我们真的没有任何东西可以指导我们为那些深奥的野兽起个好名字。它们对使用(草案)标准文本的人很重要,但不太可能成为家喻户晓的名字。我们在命名上没有发现任何真正的限制来指导我们,所以我们选择了“x”作为中心、未知、奇怪、只有专家,甚至是 x 级。

史蒂夫炫耀最终产品

回答by Dániel Sándor

INTRODUCTION

介绍

ISOC++11 (officially ISO/IEC 14882:2011) is the most recent version of the standard of the C++ programming language. It contains some new features, and concepts, for example:

ISOC++11(正式为 ISO/IEC 14882:2011)是 C++ 编程语言标准的最新版本。它包含一些新功能和概念,例如:

  • rvalue references
  • xvalue, glvalue, prvalue expression value categories
  • move semantics
  • 右值引用
  • xvalue、glvalue、prvalue 表达式值类别
  • 移动语义

If we would like to understand the concepts of the new expression value categories we have to be aware of that there are rvalue and lvalue references. It is better to know rvalues can be passed to non-const rvalue references.

如果我们想了解新表达式值类别的概念,我们必须知道有右值和左值引用。最好知道右值可以传递给非常量右值引用。

int& r_i=7; // compile error
int&& rr_i=7; // OK

We can gain some intuition of the concepts of value categories if we quote the subsection titled Lvalues and rvalues from the working draft N3337 (the most similar draft to the published ISOC++11 standard).

如果我们引用工作草案 N3337(与已发布的 ISOC++11 标准最相似的草案)中标题为 Lvalues 和 rvalues 的小节,我们可以获得对值类别概念的一些直觉。

3.10 Lvalues and rvalues [basic.lval]

1 Expressions are categorized according to the taxonomy in Figure 1.

  • An lvalue (so called, historically, because lvalues could appear on the left-hand side of an assignment expression) designates a function or an object. [ Example: If E is an expression of pointer type, then *E is an lvalue expression referring to the object or function to which E points. As another example, the result of calling a function whose return type is an lvalue reference is an lvalue. —end example ]
  • An xvalue (an “eXpiring” value) also refers to an object, usually near the end of its lifetime (so that its resources may be moved, for example). An xvalue is the result of certain kinds of expressions involving rvalue references (8.3.2). [ Example: The result of calling a function whose return type is an rvalue reference is an xvalue. —end example ]
  • A glvalue (“generalized” lvalue) is an lvalue or an xvalue.
  • An rvalue (so called, historically, because rvalues could appear on the right-hand side of an assignment expression) is an xvalue, a
    temporary object (12.2) or subobject thereof, or a value that is not
    associated with an object.
  • A prvalue (“pure” rvalue) is an rvalue that is not an xvalue. [ Example: The result of calling a function whose return type is not a
    reference is a prvalue. The value of a literal such as 12, 7.3e5, or
    true is also a prvalue. —end example ]

Every expression belongs to exactly one of the fundamental classifications in this taxonomy: lvalue, xvalue, or prvalue. This property of an expression is called its value category.

3.10 左值和右值 [basic.lval]

1 表达式根据图 1 中的分类法进行分类。

  • 左值(历史上如此称呼,因为左值可以出现在赋值表达式的左侧)指定一个函数或一个对象。[ 示例:如果 E 是指针类型的表达式,则 *E 是一个左值表达式,指的是 E 指向的对象或函数。再举一个例子,调用一个返回类型为左值引用的函数的结果是一个左值。—结束示例]
  • xvalue(一个“eXpiring”值)也指一个对象,通常接近其生命周期的终点(例如,它的资源可能会被移动)。xvalue 是某些类型的涉及右值引用的表达式的结果 (8.3.2)。[ 示例:调用返回类型为右值引用的函数的结果是 xvalue。—结束示例]
  • 左值(“广义”左值)是左值或 x 值。
  • 右值(历史上如此称呼,因为右值可能出现在赋值表达式的右侧)是一个 xvalue、一个
    临时对象 (12.2) 或其子对象,或者一个
    与对象无关的值。
  • 纯右值(“纯”右值)是不是 xvalue 的右值。[ 示例:调用返回类型不是
    引用的函数的结果是纯右值。诸如 12、7.3e5 或
    true之类的文字值也是纯右值。—结束示例]

每个表达式都完全属于此分类法中的基本分类之一:lvalue、xvalue 或 prvalue。表达式的此属性称为其值类别。

But I am not quite sure about that this subsection is enough to understand the concepts clearly, because "usually" is not really general, "near the end of its lifetime" is not really concrete, "involving rvalue references" is not really clear, and "Example: The result of calling a function whose return type is an rvalue reference is an xvalue." sounds like a snake is biting its tail.

但我不太确定这一小节是否足以清楚地理解这些概念,因为“通常”并不是真正通用的,“接近其生命周期的尽头”并不是真正具体的,“涉及右值引用”并不是很清楚,和“示例:调用返回类型为右值引用的函数的结果是 xvalue。” 听起来像一条蛇在咬它的尾巴。

PRIMARY VALUE CATEGORIES

主要价值类别

Every expression belongs to exactly one primary value category. These value categories are lvalue, xvalue and prvalue categories.

每个表达式都只属于一个主要值类别。这些值类别是左值、xvalue 和 prvalue 类别。

lvalues

左值

The expression E belongs to the lvalue category if and only if E refers to an entity that ALREADY has had an identity (address, name or alias) that makes it accessible outside of E.

表达式 E 属于左值类别当且仅当 E 指代一个实体,该实体已经具有身份(地址、名称或别名),使其可以在 E 之外访问。

#include <iostream>

int i=7;

const int& f(){
    return i;
}

int main()
{
    std::cout<<&"www"<<std::endl; // The expression "www" in this row is an lvalue expression, because string literals are arrays and every array has an address.  

    i; // The expression i in this row is an lvalue expression, because it refers to the same entity ...
    i; // ... as the entity the expression i in this row refers to.

    int* p_i=new int(7);
    *p_i; // The expression *p_i in this row is an lvalue expression, because it refers to the same entity ...
    *p_i; // ... as the entity the expression *p_i in this row refers to.

    const int& r_I=7;
    r_I; // The expression r_I in this row is an lvalue expression, because it refers to the same entity ...
    r_I; // ... as the entity the expression r_I in this row refers to.

    f(); // The expression f() in this row is an lvalue expression, because it refers to the same entity ...
    i; // ... as the entity the expression f() in this row refers to.

    return 0;
}

xvalues

The expression E belongs to the xvalue category if and only if it is

表达式 E 属于 xvalue 类别当且仅当它是

— the result of calling a function, whether implicitly or explicitly, whose return type is an rvalue reference to the type of object being returned, or

— 调用函数的结果,无论是隐式还是显式,其返回类型是对返回的对象类型的右值引用,或

int&& f(){
    return 3;
}

int main()
{
    f(); // The expression f() belongs to the xvalue category, because f() return type is an rvalue reference to object type.

    return 0;
}

— a cast to an rvalue reference to object type, or

— 对对象类型的右值引用的强制转换,或

int main()
{
    static_cast<int&&>(7); // The expression static_cast<int&&>(7) belongs to the xvalue category, because it is a cast to an rvalue reference to object type.
    std::move(7); // std::move(7) is equivalent to static_cast<int&&>(7).

    return 0;
}

— a class member access expression designating a non-static data member of non-reference type in which the object expression is an xvalue, or

— 指定非引用类型的非静态数据成员的类成员访问表达式,其中对象表达式是 xvalue,或

struct As
{
    int i;
};

As&& f(){
    return As();
}

int main()
{
    f().i; // The expression f().i belongs to the xvalue category, because As::i is a non-static data member of non-reference type, and the subexpression f() belongs to the xvlaue category.

    return 0;
}

— a pointer-to-member expression in which the first operand is an xvalue and the second operand is a pointer to data member.

— 指向成员的指针表达式,其中第一个操作数是 xvalue,第二个操作数是指向数据成员的指针。

Note that the effect of the rules above is that named rvalue references to objects are treated as lvalues and unnamed rvalue references to objects are treated as xvalues; rvalue references to functions are treated as lvalues whether named or not.

请注意,上述规则的效果是,对对象的命名右值引用被视为左值,对对象的未命名右值引用被视为 xvalues;无论是否命名,对函数的右值引用都被视为左值。

#include <functional>

struct As
{
    int i;
};

As&& f(){
    return As();
}

int main()
{
    f(); // The expression f() belongs to the xvalue category, because it refers to an unnamed rvalue reference to object.
    As&& rr_a=As();
    rr_a; // The expression rr_a belongs to the lvalue category, because it refers to a named rvalue reference to object.
    std::ref(f); // The expression std::ref(f) belongs to the lvalue category, because it refers to an rvalue reference to function.

    return 0;
}

prvalues

右值

The expression E belongs to the prvalue category if and only if E belongs neither to the lvalue nor to the xvalue category.

表达式 E 属于纯右值范畴当且仅当 E 既不属于左值也不属于 xvalue 范畴。

struct As
{
    void f(){
        this; // The expression this is a prvalue expression. Note, that the expression this is not a variable.
    }
};

As f(){
    return As();
}

int main()
{
    f(); // The expression f() belongs to the prvalue category, because it belongs neither to the lvalue nor to the xvalue category.

    return 0;
}

MIXED VALUE CATEGORIES

混合价值类别

There are two further important mixed value categories. These value categories are rvalue and glvalue categories.

还有两个重要的混合值类别。这些值类别是右值和左值类别。

rvalues

右值

The expression E belongs to the rvalue category if and only if E belongs to the xvalue category, or to the prvalue category.

表达式 E 属于右值类别当且仅当 E 属于 xvalue 类别或纯右值类别。

Note that this definition means that the expression E belongs to the rvalue category if and only if E refers to an entity that has not had any identity that makes it accessible outside of E YET.

请注意,此定义意味着表达式 E 属于右值类别,当且仅当 E 指代一个实体,该实体没有任何身份使其可在 E YET 之外访问。

glvalues

左值

The expression E belongs to the glvalue category if and only if E belongs to the lvalue category, or to the xvalue category.

表达式 E 属于左值类别当且仅当 E 属于左值类别或 xvalue 类别。

A PRACTICAL RULE

实用规则

Scott Meyer has publisheda very useful rule of thumb to distinguish rvalues from lvalues.

Scott Meyer发表了一个非常有用的经验法则来区分右值和左值。

  • If you can take the address of an expression, the expression is an lvalue.
  • If the type of an expression is an lvalue reference (e.g., T& or const T&, etc.), that expression is an lvalue.
  • Otherwise, the expression is an rvalue. Conceptually (and typically also in fact), rvalues correspond to temporary objects, such as those returned from functions or created through implicit type conversions. Most literal values (e.g., 10 and 5.3) are also rvalues.
  • 如果您可以获取表达式的地址,则该表达式是一个左值。
  • 如果表达式的类型是左值引用(例如,T& 或 const T& 等),则该表达式是左值。
  • 否则,表达式为右值。从概念上(通常也是事实上),右值对应于临时对象,例如从函数返回的对象或通过隐式类型转换创建的对象。大多数文字值(例如,10 和 5.3)也是右值。

回答by Johannes Schaub - litb

C++03's categories are too restricted to capture the introduction of rvalue references correctly into expression attributes.

C++03 的类别过于受限,无法将右值引用正确地引入表达式属性中。

With the introduction of them, it was said that an unnamed rvalue reference evaluates to an rvalue, such that overload resolution would prefer rvalue reference bindings, which would make it select move constructors over copy constructors. But it was found that this causes problems all around, for example with Dynamic Typesand with qualifications.

随着它们的引入,据说未命名的右值引用计算为右值,这样重载决议将更喜欢右值引用绑定,这将使其选择移动构造函数而不是复制构造函数。但是发现这会导致周围的问题,例如动态类型和资格。

To show this, consider

为了证明这一点,考虑

int const&& f();

int main() {
  int &&i = f(); // disgusting!
}

On pre-xvalue drafts, this was allowed, because in C++03, rvalues of non-class types are never cv-qualified. But it is intended that constapplies in the rvalue-reference case, because here we dorefer to objects (= memory!), and dropping const from non-class rvalues is mainly for the reason that there is no object around.

在 pre-xvalue 草案中,这是允许的,因为在 C++03 中,非类类型的右值永远不会被 cv 限定。但它旨在const适用于右值引用的情况,因为在这里我们确实引用了对象(=内存!),而从非类右值中删除 const 主要是因为周围没有对象。

The issue for dynamic types is of similar nature. In C++03, rvalues of class type have a known dynamic type - it's the static type of that expression. Because to have it another way, you need references or dereferences, which evaluate to an lvalue. That isn't true with unnamed rvalue references, yet they can show polymorphic behavior. So to solve it,

动态类型的问题具有相似的性质。在 C++03 中,类类型的右值有一个已知的动态类型——它是该表达式的静态类型。因为要以另一种方式使用它,您需要引用或取消引用,它们的计算结果为左值。对于未命名的右值引用,情况并非如此,但它们可以显示多态行为。所以要解决它,

  • unnamed rvalue references become xvalues. They can be qualified and potentially have their dynamic type different. They do, like intended, prefer rvalue references during overloading, and won't bind to non-const lvalue references.

  • What previously was an rvalue (literals, objects created by casts to non-reference types) now becomes an prvalue. They have the same preference as xvalues during overloading.

  • What previously was an lvalue stays an lvalue.

  • 未命名的右值引用变为xvalues。它们可以被限定并且可能具有不同的动态类型。他们确实像预期的那样,在重载期间更喜欢右值引用,并且不会绑定到非常量左值引用。

  • 以前是 rvalue (文字,由强制转换为非引用类型创建的对象)现在变成了prvalue。它们在重载期间与 xvalues 具有相同的偏好。

  • 以前的左值仍然是左值。

And two groupings are done to capture those that can be qualified and can have different dynamic types (glvalues) and those where overloading prefers rvalue reference binding (rvalues).

并且完成了两个分组以捕获那些可以被限定并且可以具有不同动态类型 ( glvalues) 和那些重载更喜欢右值引用绑定 ( rvalues) 的分组

回答by Felix Dombek

I have struggled with this for a long time, until I came across the cppreference.com explanation of the value categories.

我为此苦苦挣扎了很长时间,直到我遇到了 cppreference.com 对value Categories 的解释。

It is actually rather simple, but I find that it is often explained in a way that's hard to memorize. Here it is explained very schematically. I'll quote some parts of the page:

它实际上相当简单,但我发现它经常以难以记忆的方式进行解释。在这里非常示意性地解释了它。我将引用页面的某些部分:

Primary categories

The primary value categories correspond to two properties of expressions:

  • has identity: it's possible to determine whether the expression refers to the same entity as another expression, such as by comparing addresses of the objects or the functions they identify (obtained directly or indirectly);

  • can be moved from: move constructor, move assignment operator, or another function overload that implements move semantics can bind to the expression.

Expressions that:

  • have identity and cannot be moved from are called lvalue expressions;
  • have identity and can be moved from are called xvalue expressions;
  • do not have identity and can be moved from are called prvalue expressions;
  • do not have identity and cannot be moved from are not used.

lvalue

An lvalue ("left value") expression is an expression that has identityand cannot be moved from.

rvalue (until C++11), prvalue (since C++11)

A prvalue ("pure rvalue") expression is an expression that does not have identityand can be moved from.

xvalue

An xvalue ("expiring value") expression is an expression that has identityand can be moved from.

glvalue

A glvalue ("generalized lvalue") expression is an expression that is either an lvalue or an xvalue. It has identity. It may or may not be moved from.

rvalue (since C++11)

An rvalue ("right value") expression is an expression that is either a prvalue or an xvalue. It can be moved from. It may or may not have identity.

主要类别

主要值类别对应于表达式的两个属性:

  • 具有身份:可以确定表达式是否与另一个表达式引用相同的实体,例如通过比较对象的地址或它们标识的函数(直接或间接获得);

  • 可以从以下移动:移动构造函数、移动赋值运算符或其他实现移动语义的函数重载可以绑定到表达式。

表示:

  • 有标识且不能移动的称为左值表达式
  • 具有标识且可以移动的称为xvalue 表达式
  • 没有身份并且可以从被称为纯右值表达式
  • 不具有身份且无法移出不使用。

左值

左值(“左值”)表达式是具有标识不能从 移动的表达式。

右值(C++11 前)、右值(C++11 起)

纯右值(“纯右值”)表达式是一种没有标识可以从.

xvalue(“到期值”)表达式是具有标识可以从.

格列卫

左值(“广义左值”)表达式是左值或 xvalue 的表达式。它有身份。它可能会或可能不会被移出。

右值 (C++11 起)

右值(“右值”)表达式是一个纯右值或 xvalue 的表达式。它可以从. 它可能有也可能没有身份。

回答by fredoverflow

How do these new categories relate to the existing rvalue and lvalue categories?

这些新类别与现有的右值和左值类别有何关联?

A C++03 lvalue is still a C++11 lvalue, whereas a C++03 rvalue is called a prvalue in C++11.

C++03 左值仍然是 C++11 左值,而 C++03 右值在 C++11 中被称为 prvalue。

回答by Mohan

One addendum to the excellent answers above, on a point that confused me even after I had read Stroustrup and thought I understood the rvalue/lvalue distinction. When you see

上面优秀答案的一个附录,即使在我阅读了 Stroustrup 并认为我理解了右值/左值区别之后,我仍然感到困惑。当你看到

int&& a = 3,

int&& a = 3,

it's very tempting to read the int&&as a type and conclude that ais an rvalue. It's not:

很容易将 theint&&作为类型读取并得出结论它a是一个右值。它不是:

int&& a = 3;
int&& c = a; //error: cannot bind 'int' lvalue to 'int&&'
int& b = a; //compiles

ahas a name and is ipso facto an lvalue.Don't think of the &&as part of the type of a; it's just something telling you what ais allowed to bind to.

a有一个名字并且事实上是一个左值。不要将&&视为a;类型的一部分。它只是告诉您a允许绑定的内容。

This matters particularly for T&&type arguments in constructors. If you write

这对于T&&构造函数中的类型参数尤其重要。如果你写

Foo::Foo(T&& _t) : t{_t} {}

Foo::Foo(T&& _t) : t{_t} {}

you will copy _tinto t. You need

您将复制_tt. 你需要

Foo::Foo(T&& _t) : t{std::move(_t)} {}if you want to move. Would that my compiler warned me when I left out the move!

Foo::Foo(T&& _t) : t{std::move(_t)} {}如果你想移动。当我忽略move!时,我的编译器会警告我吗?