题 什么是rvalues,lvalues,xvalues,glvalues和prvalues?


在C ++ 03中,表达式是一个 右值 或者 左值

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

  1. 右值
  2. 左值
  3. x值
  4. glvalue
  5. prvalue

两类已成为五大类。

  • 这些新的表达类别是什么?
  • 这些新类别如何与现有的右值和左值类别相关联?
  • C ++ 0x中的右值和左值类别是否与它们在C ++ 03中的相同?
  • 为什么需要这些新类别?是的 WG21 众神试图迷惑我们凡人?

1108
2017-08-30 15:02


起源


@Philip Potter:在C ++ 03中?是。左值可以用作右值,因为有一个标准的左值到右值的转换。 - James McNellis
@Tyler:“如果你可以分配给它,它是一个左值,否则,它是一个右值。” - >错了,你可以分配给类rvalues: string("hello") = string("world")。 - fredoverflow
请注意,这是值类别。表达式可以有更多属性。这些包括 位字段 (真假), 临时 (真/假)和 类型 (它的类型)。 - Johannes Schaub - litb
我认为弗雷德上面的链接比这里的任何答案要好。但这个链接已经死了。它被移动到: stroustrup.com/terminology.pdf - R. Martinho Fernandes
在C ++中,即使你的类型也有类型 - nielsbot


答案:


我想这篇文章可能不是那么简短的介绍: n3055

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

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

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

引用 n3055

  • 一个 左值 (所谓的,历史上, 因为左值可以出现在 作业的左侧 表达式)指定一个函数或 一个东西。 [例子:如果 E 是一个 那么指针类型的表达式 *E 是一个左值表达式 对象或功能 E 点。作为另一个例子, 调用一个函数的结果 返回类型是左值引用 左值。] 
  • 一个 x值 (一个 “eXpiring”值)也指一个 对象,通常接近其末尾 一生(所以它的资源可能 例如,被移动。 xvalue是 某些种类的结果 涉及右值的表达式 引用。 [例子:The 调用一个函数的结果 返回类型是右值引用 一个x值。]
  • 一个 glvalue   (“广义”左值)是一个 左值 或者 x值
  • 一个 右值 (所谓, 历史上,因为rvalues可以 出现在一个右侧 赋值表达式)是一个xvalue, 临时对象或 其子对象,或者是其值 与对象无关。
  • 一个 prvalue (“纯”右值)是一个右值 那不是xvalue。 [例子:The 调用一个函数的结果 返回类型不是引用是一个 prvalue]

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


531
2017-08-30 15:09



谢谢,这个答案非常有用!但是我的编译器不同意你的xvalues和prvalues的例子;他们恰恰相反。通过rvalue引用返回给我一个prvalue,并且按值返回给我一个xvalue。你让他们混淆了,还是我的试验床坏了?我尝试使用GCC 4.6.1,clang(来自svn)和MSVC,它们都表现出相同的行为。 - Kim Gräsman
糟糕,我只是按照链接注意到示例都在源代码中。我会找到我的标准副本并查看它的内容...... - Kim Gräsman
我使用这里的宏来测试各种表达式: stackoverflow.com/a/6114546/96963  可能是他们误解了事情。 - Kim Gräsman
添加xvalue不适用于移动语义。只有lvalue和rvalue,移动语义,完美前向和右值引用仍然可以正常工作。我认为xvalue只适用于decltype运算符:如果操作数表达式是xvalue,则decltype给出rvalue引用的类型。 - ligand
@MuhamedCicak“每个表达式都是左值或左值”:这是真的;而且标准(或文件n3055)并没有说它是假的。这句话被删掉的原因是你正在查看文档的两个版本之间的变化。该句被删除,因为在添加更精确的解释后它变得多余。 - max


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

FCD(n3092) 有一个很好的描述:

- 左值(所谓的,历史上,因为左值可能出现在左右   作业的左侧   表达式)指定一个函数或   一个东西。 [例子:如果E是   那么指针类型的表达式   * E是一个左值表达式,指的是E的对象或函数   点。作为另一个例子,结果   调用返回的函数   type是左值引用是一个   左值。 - 示例]

- 一个xvalue(一个   “eXpiring”值)也指一个   对象,通常接近其末尾   一生(因此它的资源可能是   例如,移动。 xvalue是   某种表达方式的结果   涉及右值参考(8.3.2)。 [   示例:调用a的结果   返回类型为的函数   右值参考是一个x值。 -结束   例子]

- 一个glvalue(“一般化”)   左值(lvalue)是左值或左值。

-   一个右值(所谓的历史,   因为右值可以出现在   作业的右侧   表达式)是一个xvalue,一个临时的   对象(12.2)或其子对象,或   与a无关的值   目的。

- 一个prvalue(“纯”rvalue)是   一个不是xvalue的rvalue。 [   示例:调用a的结果   返回类型不是a的函数   参考是一个prvalue。 a的价值   文字如12,7.3e5或true是   也是一个prvalue。 - 示例]

一切   表达式恰好属于其中之一   中国的基本分类   这个分类:lvalue,xvalue或   prvalue。这个属性   表达式称为其值   类别。 [注:讨论   第5章中的每个内置运算符   表示它的值的类别   收益率和价值类别   它期望的操作数。例如,   内置赋值运算符期望   左操作数是左值和   右操作数是一个prvalue   并产生一个左值。   用户定义的运算符是函数,   和它们的价值类别   期望和收益率由   他们的参数和返回类型。 -结束   注意

我建议你阅读整个部分 3.10左值和左值 虽然。

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

再次:

Taxonomy

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

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

为什么需要这些新类别?

因此可以定义和支持移动构造/分配。


304
2017-08-31 08:08



我喜欢这里的图表。我认为开始回答可能有用 “每个表达式都属于此分类法中的基本分类之一:左值,右值或右值。”  然后很容易使用图表来显示这三个基本类被组合起来制作glvalue和rvalue。 - Aaron McDaid


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

为什么需要这些新类别?

C ++标准包含许多处理表达式的值类别的规则。一些规则区分左值和右值。例如,当涉及到重载决策时。其他规则区分glvalue和prvalue。例如,您可以使用不完整或抽象类型的glvalue,但没有不完整或抽象类型的prvalue。在我们使用这个术语之前,实际需要区分glvalue / prvalue的规则是指lvalue / rvalue和它们无意中是错误的,或者包含了很多解释和例外的规则......“除非rvalue是由于未命名右值参考......“。因此,将glvalues和prvalues的概念作为自己的名称似乎是一个好主意。

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

我们仍然有与C ++ 98兼容的术语lvalue和rvalue。我们只是将rvalues分成两个子组,xvalues和prvalues,我们将lvalues和xvalues称为glvalues。 Xvalues是未命名的右值引用的一种新值类别。每个表达式都是以下三个中的一个:lvalue,xvalue,prvalue。维恩图看起来像这样:

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

功能示例:

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

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

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

159
2018-03-04 06:32





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

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

只有在毫无疑问安全的情况下才会移动。

这就是为什么存在这些类别的原因:能够谈论可以安全地离开它们的价值观,并谈论不存在的价值观。

在最早版本的r值参考中,运动很容易发生。  容易。当用户并不真正想要的时候,很容易隐藏移动的东西。

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

  1. 当它是临时或子对象时。 (prvalue)
  2. 当用户有 明确表示要移动它

如果你这样做:

SomeType &&Func() { ... }

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

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

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

如果它不是暂时的,你没有要求移动它,那么移动就是 错误。

显而易见的解决方案是制作 val 一个左值。这意味着你无法从中移动。好的;它被命名,所以它是一个左值。

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

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

我们拥有的是一个你可以视为左值的价值,  它是隐含的可移动的。我们称之为xvalue。

请注意,xvalues使我们获得另外两类值:

  • prvalue实际上只是前一种rvalue的新名称,即它们是rvalues  xvalues。

  • Glvalues是一组中xvalues和lvalues的并集,因为它们共享许多属性。

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


132
2017-07-03 12:30



这很有趣,但它编译?不能 Func 有退货声明? - ThomasMcLeod
@Thomas:这是一个例子;它如何创建返回值并不重要。重要的是它返回了一个 &&。 - Nicol Bolas
注意:prvalues可以在方程式的左侧,也就是 - 如 X foo(); foo() = X; ......由于这个根本原因,我不能完全遵循上面的优秀答案,因为你真的只能区分新的xvalue和旧式的prvalue,因为它可以是在lhs。 - Dan Nissenbaum
X是一个阶级; X foo(); 作为一个功能声明,和 foo() = X(); 是一行代码。 (我离开了第二组括号 foo() = X(); 在上面的评论中。)对于我刚刚发布的问题突出显示,请参阅 stackoverflow.com/questions/15482508/... - Dan Nissenbaum
@DanNissenbaum“xvalue不能在赋值表达式的左侧” - 为什么不呢?看到 ideone.com/wyrxiT - Mikhail


恕我直言,关于其含义的最佳解释给了我们 斯特劳斯 +考虑到的例子 DánielSándor 和 磨憨

斯特劳斯:

现在我非常担心。显然,我们正陷入僵局或   一团糟或两者皆有。我花了午餐时间做分析看看哪个   属性(值)是独立的。只有两个   独立财产:

  • has identity  - 即地址,指针,用户可以确定两个副本是否相同,等等。
  • can be moved from  - 即我们被允许以某种不确定但有效的状态离开“副本”的来源

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

  • iM:有身份,不能移动
  • im:具有身份并可以移动(例如,将左值转换为右值参考的结果)
  • Im:没有身份,可以从第四种可能性转移(IM:没有身份,不能被移动)不是   有用的 C++ (或者,我认为)用任何其他语言。

除了这三个基本的价值分类,我们   有两个明显的概括,对应于这两个   独立财产:

  • i:有身份
  • m:可以搬走

这导致我把这个图放在板上:    enter image description here

命名

我观察到我们只有有限的自由命名:两点指向   左边(标有 iM 和 i)是或多或少的人   形式已经打电话 lvalues 以及右边的两点   (标 m 和 Im)或多或少是形式的人   打来电话 rvalues。这必须反映在我们的命名中。那是,   左边的“腿” W 应该有与之相关的名字 lvalue 和   正确的“腿” W 应该有与之相关的名字 rvalue. 我注意到   整个讨论/问题来自于引入   右值引用和移动语义。这些概念根本不存在   在Strachey的世界组成的公正 rvalues 和 lvalues。有人   观察到的想法

  • 一切 value 是一个 lvalue 或者 rvalue
  • 一个 lvalue 不是 rvalue 和 rvalue 不是 lvalue 

深深嵌入我们的意识,非常有用的属性,和   在标准草案中可以找到这种二分法的痕迹。我们   所有人都同意我们应该保留这些属性(并制作它们   精确)。这进一步限制了我们的命名选择。我观察到了   标准库措辞使用 rvalue 意思是 m (该   概括),以便保持期望和文本   标准库的右下角点 W 应该命名    rvalue.

这引发了对命名的集中讨论。首先,我们需要决定   上 lvalue. 应该 lvalue 意思 iM 或者概括 i? LED   通过Doug Gregor,我们列出了核心语言措辞的地方   哪个词 lvalue 有资格表示一个或另一个。一个   列表是在大多数情况下以及最棘手/最脆弱的文本中制作的    lvalue 目前的意思 iM。这是左值的经典含义   因为“在过去”没有任何动静; move 是一个新颖的概念   在 C++0x。另外,命名的topleft点 W  lvalue 给我们   每个价值都是的财产 lvalue 或者 rvalue但不是两者兼而有之。

所以,左上角的点 W 是 lvalue 和右下角   是 rvalue. 左下角和右上角是什么原因造成的?   左下角是经典左值的推广,   允许移动。所以这是一个 generalized lvalue. 我们命名了它    glvalue. 您可以对缩写进行狡辩,但(我认为)不是   与逻辑。我们认为在认真使用 generalized lvalue   无论如何都会以某种方式缩写,所以我们最好这样做   立即(或冒险混淆)。 W的右上角较少   一般而不是右下角(现在,一如既往地称为 rvalue)。那   point代表您可以移动的对象的原始纯概念   因为它不能再次引用(除了析构函数)。   我喜欢这句话 specialized rvalue 与之相反 generalized lvalue 但 pure rvalue 缩写为 prvalue 赢了(和   可能是正确的)。所以,W的左腿是 lvalue 和    glvalue 右腿是 prvalue 和 rvalue. 顺便,   每个值都是glvalue或prvalue,但不是两者都有。

这留下了中间的顶部 Wim;也就是说,具有的价值   身份和可以移动。我们真的没有任何指导   我们为那些神秘的野兽命名。它们很重要   使用(草案)标准文本的人,但不太可能   成为家喻户晓的名字。我们没有发现任何真正的限制   命名指导我们,所以我们选择'x'作为中心,未知,   奇怪的是,xpert只有,甚至x级。

Steve showing off the final product


94
2018-01-20 13:46



是的,如果你想了解它们的意思,最好阅读C ++ comitee的原始提案和讨论,而不是标准:D - Ivan Kush
文字没有身份,也无法移动;它们仍然有用。 - DrPizza
我只想澄清一件事。 int && f(){return 1; }和MyClass && g(){return MyClass();返回xvalue,对吗?然后我在哪里可以找到表达式f()的身份;和“g();”?它们具有标识,因为return语句中有另一个表达式,它引用了与它们所引用的相同的对象 - 我理解它是对的吗? - Dániel Sándor
@DrPizza根据标准:字符串文字是 lvalues,所有其他文字都是 prvalue秒。严格地说,你可以提出一个论据,说非字符串文字应该是不可移动的,但这不是标准的编写方式。 - Brian Vandenberg


介绍

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

  • 右值参考
  • xvalue,glvalue,prvalue表达式值类别
  • 移动语义

如果我们想要理解新表达式值类别的概念,我们必须知道有rvalue和左值引用。 最好知道rvalues可以传递给非const rvalue引用。

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

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

3.10左值和右值[basic.lval]

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

  • 左值(所谓的历史,因为左值可能出现在赋值表达式的左侧)指定一个函数   或一个物体。 [例子:如果E是指针类型的表达式,那么   * E是一个左值表达式,指的是E指向的对象或函数。另一个例子,调用函数的结果   其返回类型是左值引用是左值。 - 示例]
  • xvalue(“eXpiring”值)也指对象,通常接近其生命周期的末尾(以便可以移动其资源,   例)。 xvalue是某些表达式的结果   涉及右值参考(8.3.2)。 [例子:调用的结果   返回类型为右值引用的函数是xvalue。 -结束   例子]
  • glvalue(“广义”左值)是左值或x值。
  • 一个rvalue(历史上所谓的,因为rvalues可能出现在赋值表达式的右侧)是一个x值,一个
      临时对象(12.2)或其子对象,或非对象的值
      与对象相关联。
  • prvalue(“纯”rvalue)是一个不是xvalue的rvalue。 [示例:调用返回类型不是的函数的结果
      参考是一个prvalue。文字的值,如12,7.3e5或
      true也是一个prvalue。 - 示例]

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

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

主要价值类别

每个表达式都属于一个主要值类别。这些值类别是左值,右值和右值类别。

左值

当且仅当E指的是ALREADY具有使其可在E外部访问的标识(地址,名称或别名)的实体时,表达式E属于左值类别。

#include <iostream>

int i=7;

const int& f(){
    return i;
}

int main()
{
    std::cout<<&"www"<<std::endl; // This address ...
    std::cout<<&"www"<<std::endl; // ... and this address are the same.
    "www"; // The expression "www" in this row is an lvalue expression, because it refers to the same entity ...
    "www"; // ... as the entity the expression "www" in this row refers to.

    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

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

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

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;
}

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

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;
}

- 一个类成员访问表达式,指定非引用类型的非静态数据成员,其中对象表达式是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;
}

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

请注意,上述规则的效果是对对象的命名rvalue引用被视为lvalues,对对象的未命名rvalue引用被视为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

当且仅当E既不属于左值也不属于xvalue类别时,表达式E属于prvalue类别。

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;
}

混合价值类别

还有两个重要的混合价值类别。这些值类别是rvalue和glvalue类别。

右值

当且仅当E属于xvalue类别或属于prvalue类别时,表达式E属于rvalue类别。

请注意,此定义表示当且仅当E指的是没有任何标识使其可在E YET之外访问的实体时,表达式E属于右值类别。

glvalues

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

一个实用的规则

斯科特迈耶有 发表 一个非常有用的经验法则来区分右值和左值。

  • 如果可以获取表达式的地址,则表达式为左值。
  • 如果表达式的类型是左值引用(例如,T&或const T&等),则该表达式是左值。
  • 否则,表达式是右值。从概念上(通常也实际上),rvalues对应于临时对象,例如   作为从函数返回或通过隐式类型创建的   转换。大多数文字值(例如10和5.3)也是右值。

34
2017-08-30 16:46



我仍然没有得到gvalue :(,如果你提供了类似于其他类别的真棒示例,那会有所帮助:) - Angelus Mortis
lvalues的所有示例和xvalues的所有示例都是glvalues的示例。谢谢你的编辑! - Dániel Sándor
那么为什么gvalue引入标准我想知道? ,我的意思是只有左值,右值,x值和prvalue才足够。再次感谢 :) - Angelus Mortis
你是对的。三个主要值类别是足够的。 Rvalue也不是必需的。我认为rvalue和glvalue是方便的标准。 - Dániel Sándor
很难理解 struct As{void f(){this;}} 该 this 变量是一个prvalue。我想 this 应该是一个左值。直到标准9.3.2说:在非静态(9.3)成员函数的主体中,关键字this是一个prvalue表达式。 - r0ng


C ++ 03的类别太受限制,无法正确地将rvalue引用引入表达式属性。

随着它们的引入,据说一个未命名的右值引用求值为rvalue,这样重载解析更喜欢rvalue引用绑定,这将使它选择移动构造函数而不是复制构造函数。但是人们发现这会导致周围的问题,例如 动态类型 并具备资格。

为了表明这一点,请考虑

int const&& f();

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

在pre-xvalue草案中,允许这样做,因为在C ++ 03中,非类类型的rvalues永远不会被cv限定。但它的目的是 const 适用于rvalue-reference案例,因为我们在这里  引用对象(=内存!),从非类rvalues中删除const主要是因为周围没有对象。

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

  • 未命名的右值参考成为 xvalues。它们可以是合格的,并且可能具有不同的动态类型。它们像预期的那样在重载期间更喜欢rvalue引用,并且不会绑定到非const左值引用。

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

  • 先前的左值是一个左值。

并且完成了两个分组以捕获那些可以合格且可以具有不同动态类型的分组(glvalues和那些重载的人更喜欢右值参考绑定(右值)。


32
2018-06-17 02:20



答案显然是合理的。 xvalue只是rvalue,可以是cv-qualified和动态类型! - ligand


我很长时间以来一直在努力,直到我遇到了cppreference.com的解释 价值类别

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

主要类别

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

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

  • 可以搬走:移动构造函数,移动赋值运算符,或实现移动语义的另一个函数重载可以绑定到表达式。

表达:

  • 有身份,不能被调动 左值表达式;
  • 有身份,可以从被调动 xvalue表达式;
  • 没有身份,可以被调动 prvalue表达式;
  • 没有身份,不能被移动不被使用。

左值

左值(“左值”)表达式是一个表达式 有身份 和 无法移动

rvalue(直到C ++ 11),prvalue(自C ++ 11起)

prvalue(“纯rvalue”)表达式是一种表达式 没有身份 和 可以搬走

x值

xvalue(“expiring value”)表达式是一个表达式 有身份 和 可以搬走

glvalue

glvalue(“generalized lvalue”)表达式是一个左值或x值的表达式。它 有身份。它可能会或可能不会被移动。

rvalue(自C ++ 11起)

rvalue(“right value”)表达式是prvalue或xvalue的表达式。它 可以搬走。它可能有也可能没有身份。


21
2017-08-30 15:45



在一些书中,xvalues显示其x来自“专家”或“特殊” - noɥʇʎԀʎzɐɹƆ
更重要的是,他们全面的例子清单。 - Ciro Santilli 新疆改造中心 六四事件 法轮功


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

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


15
2017-07-25 04:26