3

对于这个问题的广泛性,我很抱歉,只是所有这些细节都是紧密相连的..

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

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

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

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

然而,这种心智模型至少不适用于临时对象,据我所知,它们应该始终在实际内存中创建(例如,如果像fun(MyClass())使用 prvalue 参数一样调用 rvalue-ref-taking 函数)。所以我猜这个心智模型是错误的。

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

与此相关的另一个来源是这里的答案: 什么是移动语义?

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

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

4

2 回答 2

6

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

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

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

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

在实践中:

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

在此示例中,评估foo()将导致prvalue. 仅通过查看此表达式,您就知道该对象是作为 return 的一部分创建的,foo并将在此完整表达式的末尾被销毁。因为你知道所有这些事情,所以你可以为它的一生做序:

const X& rx = foo();

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

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

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

const X& rx = bar();

这不会延长使用寿命。

于 2017-07-26T09:43:30.460 回答
0

当打电话给func(T&& t)来电者说“这里有”和“我不在乎你对它做了什么”。C++ 没有指定“这里”的性质。

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

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

于 2017-07-26T08:45:12.280 回答