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

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

移动语义的想法是您可以从另一个临时对象(由右值引用引用)中获取所有内容并将其存储在您的对象中 "everything"。这有助于避免在事物的单一构造就足够的情况下进行深度复制——因此您在右值对象中构造事物,然后将其移动到长期存在的对象中。

为什么 C++ 不允许将左值对象绑定到右值引用?两者都允许我更改引用对象,因此在访问引用对象的内部方面对我来说没有区别。

我能猜到的唯一原因是函数重载歧义问题。

But why C++ doesn't allow binding lvalue objects to rvalue references?

假设您的意思是 "Why doesn't C++ allow binding rvalue references to lvalue objects":是的。它只是不是自动的,因此您必须使用 std::move 使其明确。

为什么?因为否则一个无伤大雅的函数调用可能会出人意料地破坏你意想不到的东西:

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

对比

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

关于破坏性复制的注意事项:为什么我在上面说 破坏性破坏性,我并不是说对象析构函数结束了它的生命周期: 只是它的内部状态已经被移动到一个新的实例。它仍然是一个有效的对象,但不再像以前那样保持昂贵的状态。


关于术语的注释:让我们看看是否可以消除上面 lvaluervalue 等的不精确使用。

后代引用cppreference

  • 一个lvalue

    an expression that has identity and cannot be moved from.

    所以,没有左值对象这样的东西,但是有一个对象在本地由左值表达式命名(或引用)

  • 一个rvalue

    an expression that is either a prvalue or an xvalue. It can be moved from. It may or may not have identity.

    • a prvalue(纯右值)大致是一个引用未命名临时对象的表达式:我们无法将左值表达式转换为以下之一这些 IIUC.

    • 一个xvalue(到期值)是

      an expression that has identity and can be moved from.

      其中明确包含 std::move

    • 的结果

所以实际发生了什么:

  • 一个对象存在
  • 对象由左值表达式在本地标识,不能从中移动(以保护我们免受意外副作用)
  • std::move 产生一个 xvalue 表达式(可以从中移动)引用与 lvalue 表达式相同的对象
  • 这意味着诸如变量之类的对象(由左值表达式命名)不能隐式移动,而必须显式移动来自显式的 xvalue 表达式,例如 std::move.
  • 匿名临时变量可能已经被 prvalue 表达式引用,并且可以隐式移动

本质上,需要一种机制来区分可以移动的值和不能移动的值(即需要一个副本)。

允许右值和左值都绑定到左值引用使得这不可能。

因此,绑定到右值引用的值可以被移动(不一定总是被移动,但它是允许的),左值可以被绑定到左值引用并且不能被移动。

std::move 是否允许在值类别之间进行转换(到右值)以允许移动发生。

注意; const 左值引用 (const T&) 可以绑定到右值(临时值)和左值,因为所引用的对象不能更改(它被标记为 const 所以无论如何都不能移动).

有一些历史(可以追溯到 C++ 的早期)为什么临时对象不能绑定到非 const 左值引用开始...细节是模糊的,但有一些理由可以修改临时 didn't make sense, since it would destruct at the end of the current statement anyway. 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 绑定到地址、文字等。这是在移动之前,其语义固化,并且是移动及其语义的一些动机。