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

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

本文关键字:引用 绑定 C++ 对象 不能 为什么      更新时间:2023-10-16

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

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

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

但是为什么C++不允许将左值对象绑定到右值引用呢?

假设你的意思是"为什么C++不允许将右值引用绑定到左值对象":确实如此。它只是不是自动的,因此您必须使用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

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


关于术语的说明:让我们看看是否可以清除上面左右值等的不精确使用。

引用后人的话:

  • 左值是

    具有标识无法从中移动的表达式。

    因此,没有左值对象

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

  • 右值是

    作为 PR值或 XValue 的表达式。它可以从中移动。它可能具有也可能没有标识。

    • prvalue(纯右值)大致是一个引用未命名临时对象的表达式:我们不能将我们的 lvalue 表达式转换为这些 IIUC 之一。

    • x值(即将过期的值)为

      具有标识可从中移动的表达式。

      明确包括std::move的结果

那么实际发生了什么:

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

本质上,需要一种机制来区分可以从中移动的值和无法移动的值(即需要副本)。

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

因此,绑定到右值引用的值可以从中移动(不一定总是从中移动,

但这是允许的),并且左值可以绑定到左值引用,但不能从中移动。

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

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

有一些历史(可以追溯到 C++ 的早期)为什么临时对象不能绑定到非常量左值引用......细节很模糊,但有一些理由认为修改临时没有意义,因为它无论如何都会在当前语句的末尾破坏。此外,您可能会被哄骗到您正在修改左值的感觉中,而实际上并非如此 - 代码的语义可能/将是错误的并且有问题。还有其他原因与地址、文字等有关。这是在移动之前,其语义固化,并且是移动及其语义的一些动机。