【问题标题】:xvalues vs prvalues: what does identity property addxvalues vs prvalues:身份属性添加了什么
【发布时间】:2018-01-01 05:36:39
【问题描述】:

我很抱歉这个问题太宽泛了,只是所有这些细节都紧密相连..

我一直试图了解具体两个值类别之间的区别 - xvalues 和 prvalues,但我仍然感到困惑。

无论如何,我试图为自己开发的“身份”概念的心智模型是,应该保证拥有它的表达式驻留在实际程序的数据内存中。

出于这个原因,字符串文字是左值,它们保证在整个程序运行期间驻留在内存中,而数字文字是纯右值并且可以例如假设存储在直接 asm 中。

这似乎同样适用于纯右值文字中的std::move,即当调用fun(1) 时,我们只会在被调用者框架中获得参数左值,但是当调用fun(std::move(1)) 时,必须保留glvalue 的xvalue '种类'在调用者框架中。

但是,这种心智模型至少不适用于临时对象,据我所知,它们应该始终在实际内存中创建(例如,如果像 fun(MyClass()) 那样调用带有右值的右值引用函数争论)。所以我猜这个心智模型是错误的。

那么思考 xvalue 的“身份”属性的正确方法是什么?我已经读过,我可以通过身份比较地址,但是如果我可以比较 2 个MyClass().members 的地址(根据 cppreference 的 xvalue),假设通过 rvalue refs 将它们传递给某个比较函数,那么我不明白为什么我不能对 2 MyClass()s (prvalue) 做同样的事情?

与此相关的另一个来源是这里的答案: What are move semantics?

请注意,即使 std::move(a) 是一个右值,它的求值也不会创建一个临时对象。这个难题迫使委员会引入第三个价值类别。可以绑定到右值引用的东西,即使它不是传统意义上的右值,也称为 xvalue(eXpiring 值)。

但这似乎与“可以比较地址”无关,并且 a) 我看不出这与右值的“传统意义”有何不同; b)我不明白为什么这样的原因需要语言中的新值类别(好吧,这允许为 OO 意义上的对象提供动态类型,但 xvalues 不仅仅指对象)。

【问题讨论】:

  • 注意新规则 (c++17) 重新定义了纯右值并引入了纯右值到极值的转换,“临时物化”。如果您尝试将两个 MyClass() 纯右值传递给比较函数,它们将作为引用初始化的一部分进行此转换。
  • @Cubbi 嗯,你的意思是 C++17 还与我试图提出的这个“保证存储在内存中”的概念更加一致,或者实现的理由纯粹是保证省略?我知道这个概念与标准的字眼相去甚远,但从外部角度来看,它似乎确实符合事物的定义方式。
  • 似乎更一致,是的。 C++14 rvalue ref 将“直接”绑定到类纯右值和类 xvalue 初始化程序。 ([dcl.init.ref]/5.2)。现在它直接绑定到 xvalues,但转换 prvalues 并绑定到结果 xvalues。

标签: c++ c++14 xvalue value-categories prvalue


【解决方案1】:

我个人有另一个心智模型,它不直接处理身份和记忆等等。

prvalue 来自“纯右值”,而xvalue 来自“到期值”,这是我在心智模型中使用的信息吗:

纯右值指的是一个“纯粹意义上”的临时对象:一个表达式,编译器可以绝对肯定地告诉它它的评估是一个临时对象,具有刚刚创建并且立即到期(除非我们通过引用绑定进行干预以延长其生命周期)。该对象是在表达式求值期间创建的,它将按照“母表达式”的规则死亡。

相比之下,expiring value 是一个表达式,其计算结果是对一个承诺即将过期的对象的引用。那就是它给了你一个承诺,你可以对这个对象做任何你想做的事情,因为它无论如何都会被销毁。但是你不知道这个对象是什么时候创建的,或者它应该什么时候被销毁。你只知道你“拦截”了它,因为它快要死了。

在实践中:

struct X;
auto foo() -> X;
X x = foo();
      ^~~~~

在此示例中,评估 foo() 将得到 prvalue。只需查看这个表达式,您就知道这个对象是作为foo 返回的一部分创建的,并将在这个完整表达式的末尾被销毁。因为您知道所有这些事情,所以您可以为它的一生揭开序幕:

const X& rx = foo();

现在 foo 返回的对象的生命周期延长到 rx 的生命周期

auto bar() -> X&&
X x = bar();
      ^~~~

在此示例中,评估 bar() 将得到 xvaluebar 承诺你给你一个即将过期的对象,但你不知道这个对象是什么时候创建的。它可以在调用bar 之前创建(作为临时的或不是临时的),然后bar 给你一个rvalue reference 给它。好处是你知道你可以用它做任何你想做的事情,因为它不会被使用后记(例如你可以离开它)。但是你不知道这个对象什么时候应该被销毁。因此,您无法延长它的生命周期 - 因为您首先不知道它的原始生命周期是什么:

const X& rx = bar();

这不会延长使用寿命。

【讨论】:

    【解决方案2】:

    当呼叫func(T&& t) 时,呼叫者会说“这里有一个 t”以及“我不在乎你对它做了什么”。 C++ 没有指定“这里”的性质。

    在将引用参数实现为地址的平台上,这意味着某处必须存在对象。在该平台上的身份 == 地址。然而,这不是语言的要求,而是平台调用约定的要求。

    平台可以简单地通过安排对象以特定方式在调用者和被调用者中注册来实现引用。这里的身份可以是“注册 edi”。

    【讨论】:

      猜你喜欢
      • 2017-07-15
      • 1970-01-01
      • 1970-01-01
      • 2016-10-15
      • 1970-01-01
      • 2013-01-26
      • 2021-11-21
      • 2013-11-02
      • 2013-03-13
      相关资源
      最近更新 更多