简体   繁体   English

为什么C ++左值对象不能绑定到右值引用(&&)?

[英]Why C++ lvalue objects can't be bound to rvalue references (&&)?

The idea of move semantics is that you can grab everything from another temporary object (referenced by an rvalue reference) and store that "everything" in your object. 移动语义的想法是,您可以从另一个临时对象(由右值引用引用)中获取所有内容,并将“所有内容”存储在对象中。 That helps to avoid deep copying where single construction of things is enough -- so you construct things in a rvalue object and then just move it to your long living object. 这有助于避免在单个构造事物足够的情况下进行深度复制 - 因此您可以在rvalue对象中构造事物,然后将其移动到长寿命对象中。

Why is it that C++ doesn't allow binding lvalue objects to rvalue references? 为什么C ++不允许将左值对象绑定到右值引用? Both allow me to change the referenced object, so there is no difference to me in terms of accessing internals of referenced object. 两者都允许我更改引用的对象,因此在访问引用对象的内部方面对我没有任何区别。

The only reason I can guess is function overloading ambiguity issues. 我能猜到的唯一原因是函数重载模糊问题。

But why C++ doesn't allow binding lvalue objects to rvalue references? 但是为什么C ++不允许将左值对象绑定到右值引用?

Assuming you mean "Why doesn't C++ allow binding rvalue references to lvalue objects" : it does. 假设你的意思是“为什么C ++不允许将右值引用绑定到左值对象” :它确实如此。 It just isn't automatic, so you have to use std::move to make it explicit. 它不是自动的,所以你必须使用std::move来使它显式化。

Why? 为什么? Because otherwise an innocuous function call can surprisingly destroy something you didn't expect it to: 因为否则一个无害的函数调用会令人惊讶地破坏你没想到的东西:

Class object(much,state,many,members,wow);
looks_safe_to_me(object);
// oh no, it destructively copied my object!

vs.

Class object(much,state,many,members,wow);
obviously_destructive(std::move(object));
// same result, but now the destruction is explicit and expected

A note on destructive copying: why I say destructively and destruction above, I don't mean the object destructor ends its lifetime: just that its internal state has been moved to a new instance. 关于破坏性复制的注释:为什么我说上面的破坏性破坏 ,我并不是说对象析构函数结束它的生命周期:只是它的内部状态已被移动到一个新实例。 It's still a valid object, but no longer holds the same expensive state it used to. 它仍然是一个有效的对象,但不再拥有以前相同的昂贵状态。


A note on terminology: let's see if we can clear up the imprecise use of lvalue , rvalue etc. above. 关于术语的说明:让我们看看我们是否可以清除上面对左值右值等的不精确使用。

Quoting from cppreference for posterity: 引用后代的cppreference

  • an lvalue is 一个左值

    an expression that has identity and cannot be moved from . 具有身份无法移动的表达式。

    So, there's no such thing as an lvalue object, but there is an object which is locally named (or referred to) by an lvalue expression 所以,没有lvalue对象这样的东西,但是有一个由左值表达式在本地命名(或引用)的对象

  • an rvalue is 右边

    an expression that is either a prvalue or an xvalue. 一个表达式,它是一个prvalue或一个xvalue。 It can be moved from . 可以从 It may or may not have identity. 它可能有也可能没有身份。

    • a prvalue (pure rvalue) is roughly an expression referring to an un-named temporary object: we can't convert our lvalue expression to one of these IIUC. prvalue (纯rvalue)大致是指一个未命名的临时对象的表达式:我们不能将我们的左值表达式转换为这些IIUC中的一个。

    • an xvalue (expiring value) is xvalue (到期值)是

      an expression that has identity and can be moved from . 一个具有身份可以移动的表达式。

      which explicitly includes the result of std::move 其中明确包含std::move的结果

So what actually happens: 那么实际发生了什么:

  • an object exists 存在一个对象
  • the object is identified locally by an lvalue expression, which cannot be moved from (to protect us from unexpected side-effects) 该对象由左值表达式在本地标识,该表达式无法移动(以保护我们免受意外的副作用)
  • std::move yields an xvalue expression (which can be moved from) referring to the same object as the lvalue expression std::move产生一个xvalue表达式(可以从中移动)引用与左值表达式相同的对象
  • this means objects such as variables (which are named by lvalue expressions) cannot be implicitly moved from, and must instead explicitly moved from via an explicit xvalue expression such as std::move . 这意味着对象,例如变量(其由左值表达式命名)不能从隐式地移动,并且必须而是明确从经由显式表达x值移动诸如std::move
  • anonymous temporaries are probably already referred to by prvalue expressions, and can be moved implicitly 匿名临时值可能已由prvalue表达式引用,并且可以隐式移动

Essentially, a mechanism is needed to distinguish between values that can be moved from, and those that cannot be moved from (ie a copy would be needed). 本质上,需要一种机制来区分可以移动的值和不能移动的值(即需要复制)。

Allowing both rvalues and lvalues to be bound to an lvalue reference makes that impossible. 允许rvalues和lvalues绑定到左值引用使得这是不可能的。

Hence, values bound to an rvalue reference can be moved from (not necessarily always going to be moved from, but it is allowed), and lvalues can be bound to lvalue references and can't be moved from. 因此,绑定到右值引用的值可以从(不一定总是要移动,但允许)移动,并且左值可以绑定到左值引用,并且不能从中移动。

std::move is there to allow for the casting between the value categories (to an rvalue) to allow the move to happen. std::move用于允许在值类别(到rvalue)之间进行转换以允许移动发生。

Note; 注意; const lvalue references ( const T& ) can be bound to both rvalues (temporaries) and lvalues since the referred to object can't change (it is marked as const so there can't be any moving from anyway). const左值引用( const T& )可以绑定到rvalues(临时值)和左值,因为引用的对象不能更改(它被标记为const因此无论如何都不能有任何移动)。

There is some history (back to the early days of C++) to why temporary objects could not be bound to non-const lvalue references to begin with... the detail is blurry but it there was some reasoning that modifying a temporary didn't make sense , since it would destruct at the end of the current statement anyway. 有一些历史(早在C ++的早期)为什么临时对象无法绑定到非const左值引用开始... ...细节模糊但是有一些推理修改临时对象没有有道理 ,因为无论如何它会在当前声明的最后破坏。 Additionally you could be lulled into the sense that you were modifying an lvalue, when you where in fact not - the semantics of the code could/would be wrong and be buggy. 另外,当你实际上没有修改左值时,你可能会感觉到你正在修改左值 - 代码的语义可能/将是错误的并且是错误的。 There are further reasons tied to addresses, literals etc. This was before moving and the semantics thereof solidified, and is some of the motivation for moving and its semantics. 还有其他原因与地址,文字等相关联。这是在移动之前,其语义固化,并且是移动及其语义的一些动机。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM