xvalues vs prvalues:identity 属性 添加了什么
xvalues vs prvalues: what does identity property add
对于这个问题的广泛性,我很抱歉,所有这些细节都是紧密相关的..
我一直在努力理解具体两个值类别之间的区别 - xvalues 和 prvalues,但我仍然感到困惑。
无论如何,我为 'identity' 的概念尝试为自己开发的心智模型是,应该保证拥有它的表达式驻留在实际程序的数据内存中。
正因为如此,字符串文字是左值,它们保证在整个程序中驻留在内存中 运行,而数字文字是纯右值,例如假设存储在直接 asm 中。
这似乎也适用于纯右值文字中的 std::move
,即当调用 fun(1)
时,我们只会在被调用方框架中获得参数左值,但在调用 fun(std::move(1))
时,我们会得到 xvalue 'kind' 的泛左值必须保存在调用者框架中。
然而,这种心智模型至少不适用于临时对象,据我所知,临时对象应该始终在实际内存中创建(例如,如果一个右值引用函数被称为 fun(MyClass())
带有纯右值参数)。所以我猜这个心智模型是错误的。
那么考虑 xvalues 的 'identity' 属性 的正确方法是什么?我已经读过,通过身份我可以比较地址,但是如果我可以比较 2 MyClass().member
s 的地址(根据 cppreference 的 xvalue),假设通过右值引用将它们传递给某个比较函数,那么我不不明白为什么我不能对 2 MyClass()
s (prvalue) 做同样的事情?
另一个与此相关的来源是这里的答案:
What are move semantics?
Note that even though std::move(a) is an rvalue, its evaluation does not create a temporary object. This conundrum forced the committee to introduce a third value category. Something that can be bound to an rvalue reference, even though it is not an rvalue in the traditional sense, is called an xvalue (eXpiring value).
但这似乎与 'can compare addresses' 无关并且 a) 我看不出这与右值的 'traditional sense' 有何不同; b) 我不明白为什么这样的原因需要语言中的新值类别(好吧,好吧,这允许为 OO 意义上的对象提供动态类型,但 xvalues 不仅仅指对象)。
当呼叫 func(T&& t)
时,呼叫者说的是 "there's a t here" 和 "I don't care what you do to it"。 C++ 没有指定 "here".
的性质
在引用参数作为地址实现的平台上,这意味着某处必须存在一个对象。在那个平台上身份==地址。然而,这不是语言的要求,而是平台调用约定的要求。
平台可以简单地通过安排对象以特定方式在调用者和被调用者中注册来实现引用。这里的身份可以是 "register edi".
我个人有另一种心智模型,它不直接处理身份和记忆等等。
prvalue
来自 "pure rvalue",而 xvalue
来自 "expiring value",我在心智模型中使用的信息是:
Pure rvalue 指的是 "pure sense" 中的临时对象:编译器可以绝对确定地判断其求值是一个对象的表达式这是一个刚刚创建的临时文件,即将到期(除非我们通过引用绑定进行干预以延长其生命周期)。该对象是在表达式求值期间创建的,它将根据 "mother expression".
的规则消亡
相比之下,即将过期的值 是一个表达式,其计算结果是对 承诺 即将过期的对象的引用。那就是它给了你一个承诺,你可以对这个对象做任何你想做的事,因为无论如何它接下来都会被销毁。但是你不知道这个对象是什么时候创建的,或者它应该在什么时候被销毁。你只知道你 "intercepted" 它快要死了。
实践中:
struct X;
auto foo() -> X;
X x = foo();
^~~~~
在此示例中,评估 foo()
将导致 prvalue
。只需查看此表达式,您就会知道此对象是作为 foo
的 return 的一部分创建的,并将在这个完整表达式的末尾被销毁。因为您知道所有这些事情,所以您可以开启它的生命周期:
const X& rx = foo();
现在由 foo 编辑的对象 return 的生命周期延长到 rx
的生命周期
auto bar() -> X&&
X x = bar();
^~~~
在此示例中,计算 bar()
将导致 xvalue
。 bar
向你承诺给你一个即将过期的对象,但你不知道这个对象是什么时候创建的。它可以在调用 bar
之前创建(无论是否是临时的)然后 bar
给你一个 rvalue reference
给它。优点是你知道你可以用它做任何你想做的事,因为它不会在后记中使用(例如,你可以从它移动)。但是你不知道这个对象什么时候应该被销毁。因此你不能延长它的生命周期——因为你一开始就不知道它的原始生命周期是多少:
const X& rx = bar();
这不会延长寿命。
对于这个问题的广泛性,我很抱歉,所有这些细节都是紧密相关的..
我一直在努力理解具体两个值类别之间的区别 - xvalues 和 prvalues,但我仍然感到困惑。
无论如何,我为 'identity' 的概念尝试为自己开发的心智模型是,应该保证拥有它的表达式驻留在实际程序的数据内存中。
正因为如此,字符串文字是左值,它们保证在整个程序中驻留在内存中 运行,而数字文字是纯右值,例如假设存储在直接 asm 中。
这似乎也适用于纯右值文字中的 std::move
,即当调用 fun(1)
时,我们只会在被调用方框架中获得参数左值,但在调用 fun(std::move(1))
时,我们会得到 xvalue 'kind' 的泛左值必须保存在调用者框架中。
然而,这种心智模型至少不适用于临时对象,据我所知,临时对象应该始终在实际内存中创建(例如,如果一个右值引用函数被称为 fun(MyClass())
带有纯右值参数)。所以我猜这个心智模型是错误的。
那么考虑 xvalues 的 'identity' 属性 的正确方法是什么?我已经读过,通过身份我可以比较地址,但是如果我可以比较 2 MyClass().member
s 的地址(根据 cppreference 的 xvalue),假设通过右值引用将它们传递给某个比较函数,那么我不不明白为什么我不能对 2 MyClass()
s (prvalue) 做同样的事情?
另一个与此相关的来源是这里的答案: What are move semantics?
Note that even though std::move(a) is an rvalue, its evaluation does not create a temporary object. This conundrum forced the committee to introduce a third value category. Something that can be bound to an rvalue reference, even though it is not an rvalue in the traditional sense, is called an xvalue (eXpiring value).
但这似乎与 'can compare addresses' 无关并且 a) 我看不出这与右值的 'traditional sense' 有何不同; b) 我不明白为什么这样的原因需要语言中的新值类别(好吧,好吧,这允许为 OO 意义上的对象提供动态类型,但 xvalues 不仅仅指对象)。
当呼叫 func(T&& t)
时,呼叫者说的是 "there's a t here" 和 "I don't care what you do to it"。 C++ 没有指定 "here".
在引用参数作为地址实现的平台上,这意味着某处必须存在一个对象。在那个平台上身份==地址。然而,这不是语言的要求,而是平台调用约定的要求。
平台可以简单地通过安排对象以特定方式在调用者和被调用者中注册来实现引用。这里的身份可以是 "register edi".
我个人有另一种心智模型,它不直接处理身份和记忆等等。
prvalue
来自 "pure rvalue",而 xvalue
来自 "expiring value",我在心智模型中使用的信息是:
Pure rvalue 指的是 "pure sense" 中的临时对象:编译器可以绝对确定地判断其求值是一个对象的表达式这是一个刚刚创建的临时文件,即将到期(除非我们通过引用绑定进行干预以延长其生命周期)。该对象是在表达式求值期间创建的,它将根据 "mother expression".
的规则消亡相比之下,即将过期的值 是一个表达式,其计算结果是对 承诺 即将过期的对象的引用。那就是它给了你一个承诺,你可以对这个对象做任何你想做的事,因为无论如何它接下来都会被销毁。但是你不知道这个对象是什么时候创建的,或者它应该在什么时候被销毁。你只知道你 "intercepted" 它快要死了。
实践中:
struct X;
auto foo() -> X;
X x = foo();
^~~~~
在此示例中,评估 foo()
将导致 prvalue
。只需查看此表达式,您就会知道此对象是作为 foo
的 return 的一部分创建的,并将在这个完整表达式的末尾被销毁。因为您知道所有这些事情,所以您可以开启它的生命周期:
const X& rx = foo();
现在由 foo 编辑的对象 return 的生命周期延长到 rx
auto bar() -> X&&
X x = bar();
^~~~
在此示例中,计算 bar()
将导致 xvalue
。 bar
向你承诺给你一个即将过期的对象,但你不知道这个对象是什么时候创建的。它可以在调用 bar
之前创建(无论是否是临时的)然后 bar
给你一个 rvalue reference
给它。优点是你知道你可以用它做任何你想做的事,因为它不会在后记中使用(例如,你可以从它移动)。但是你不知道这个对象什么时候应该被销毁。因此你不能延长它的生命周期——因为你一开始就不知道它的原始生命周期是多少:
const X& rx = bar();
这不会延长寿命。