【问题标题】:Why C++ lvalue objects can't be bound to rvalue references (&&)?为什么 C++ 左值对象不能绑定到右值引用(&&)?
【发布时间】:2016-06-09 17:47:48
【问题描述】:

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

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

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

【问题讨论】:

  • 你在问为什么不能有void foo(vector & bar) {} foo(create temp vector here);
  • 你知道std::move 吗?
  • @NathanOliver OP 提出了相反的要求 — void foo(vector&& bar) {} foo(already existing vector); ;-)

标签: c++ c++11 move-semantics rvalue-reference


【解决方案1】:

但为什么 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

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


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

后人引用cppreference

  • lvalue

    具有标识无法移出的表达式

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

  • rvalue

    是纯右值或 xvalue 的表达式。它可以移出。它可能有也可能没有身份。

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

    • 一个xvalue(过期值)是

      一个有身份的表达式并且可以从移出。

      其中明确包含std::move的结果

那么实际发生了什么:

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

【讨论】:

  • 当您使用std::move 时,您不会将右值引用绑定到左值。您将左值转换为右值,然后将右值引用绑定到该值。
  • 中间不是一个xvalue表达式吗?无论哪种方式,最终结果都是一个右值引用给我们之前标识为左值的对象起别名...
  • @Useless 但是对象没有值类别,表达式有。表达式object 是左值,表达式std::move(object) 是右值。
  • 好点:我需要学习词汇才能清楚地讨论这个问题。
  • 我希望我可以投票两次 :-) - 我见过的最直接和最短的解释指出左值、纯右值、xvalues 之间的区别(glvalues 会使其完整)。我的意思是你的 cmets,当然不是引号。
【解决方案2】:

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

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

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

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

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

有一些历史(回到 C++ 的早期)为什么临时对象不能绑定到非 const 左值引用开始......细节很模糊,但有一些理由认为修改临时对象didn't make sense,因为无论如何它都会在当前语句的末尾销毁。此外,您可能会陷入您正在修改左值的感觉,而实际上您没有 - 代码的语义可能/将是错误的并且有问题。 There are further reasons 与地址、文字等相关联。这是在移动之前,其语义已固化,是移动的一些动机及其语义。

【讨论】:

  • 我的总体印象是,在尝试修复某些问题时,C++ 委员会只会造成更大的混乱。在之前或T&& 的故事中,可以使用引用来修改传递的对象(发生什么样的修改并不重要)。这意味着在传递非 const 引用的每种情况下,都应该检查传递的实体发生了什么。他们实际上试图建立不应修改 T& 的概念(即不应采取其内容)。观念很差。应该使用const T&
  • 其复杂规则的最终效果将是相反的。将编写大量错误代码,因为许多程序员不会尝试理解语言设计的微小细节,而是编写somethingsomehow。是的,有时他们的代码会起作用。在其他情况下 - 像往常一样。
猜你喜欢
  • 2018-01-13
  • 2017-04-15
  • 2011-02-14
  • 1970-01-01
  • 1970-01-01
  • 2014-01-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多