【问题标题】:Why this two rvalue references examples have different behavior?为什么这两个右值引用示例具有不同的行为?
【发布时间】:2016-02-10 21:10:51
【问题描述】:

第一个例子

int a = 0;
auto && b = ++a;
++a;
cout << a << b << endl;

打印22

第二个例子

int a = 0;
auto && b = a++;
++a;
cout << a << b << endl;

打印20

问题: 为什么在第一个示例中第三行中的++a 也会增加b,为什么在第二个示例中没有这种行为?

更新:New question 出现了。

【问题讨论】:

  • 我会给你一个提示 - a 不是右值。
  • @erip 是的,我知道,但是b 的两个示例有什么区别?
  • 在第二个示例中,b 获取 a 的值,而之前没有增加它,就像在第一个示例中一样。

标签: c++ rvalue-reference


【解决方案1】:

因为预增量 (++a) 首先递增 a 的值,存储结果,然后 然后 返回对 a 的引用。现在ab 有效地指向同一个对象。

然而,后增量 (a++) 首先将 a 的当前值存储在一个临时值中,递增 a,然后返回这个临时值 - 您的右值 ref 指向的值。 ab 指向不同的对象,更具体地说 - b 是在递增之前临时保存 a 的值。

这就是为什么鼓励使用++it 而不是it++ 来表示迭代器和其他定义递增/递减的复杂对象的原因:后者会创建一个临时副本,因此可能会更慢。

【讨论】:

  • 第二个例子中a++之后使用b是不是UB?
  • 为什么在第一种情况下没有临时的?为什么auto &amp;&amp; b = a++ 不等同于auto &amp;&amp; b = a; a++
  • @vladon:我认为临时文件将一直存在,直到对它的引用超出范围,这意味着它不是 UB,但我不确定。
  • @vladon:因为标准说++a 返回一个引用:) 现在,为什么它是这样设计的,这是一个不同的问题。我认为它既更快(不涉及复制)又提供了额外的灵活性(您可以继续在 ++ 结果上链接调用)。
  • @vladon:我认为你不能在这里提到我的原因是因为无论如何我都会收到关于我的答案的任何 cmets 的通知。在我的帖子中提及我是多余的。
【解决方案2】:

区别在于++a 是左值,而a++ 不是。这是由 C++14 [expr.pre.incr]/1 指定的:

前缀++的操作数修改为加1 [...] 操作数应为可修改的左值。 [...] 结果是更新的操作数;它是一个左值

和[expr.post.incr]/1:

[...] 结果是纯右值。


现在我们考虑 auto &amp;&amp; b = ++a;++a 是一个左值。 auto&amp;&amp; 是转发引用。转发引用实际上可以绑定到左值:auto 本身可以推断为引用类型。这段代码推导出为int &amp;b = ++a;

当引用绑定到同类型的左值时,引用直接绑定,所以b成为a的另一个名字。


在第二个示例中,auto &amp;&amp; b = a++;a++ 是纯右值。这意味着它没有关联的地址,并且不再与变量a 有任何关系。该行与++a; auto &amp;&amp; b = (a + 0); 的行为相同。

首先,由于a++ 是prvalue,auto&amp;&amp; 推导出为int&amp;&amp;。 (即auto 推导出为int)。当非类类型的引用绑定到纯右值时,临时对象将从该值复制初始化。此对象的生命周期已延长以匹配引用。

因此,在第二种情况下,b 绑定到与 a 不同的对象,即“临时” int(实际上并不是那么临时,因为它的持续时间与 b 一样长)。

引用绑定规则在[dcl.init.ref]中。

【讨论】:

    【解决方案3】:

    在第二种情况下(后增量)b实际上引用了为(a++)创建的临时,因此增量不会影响b。

    【讨论】:

      猜你喜欢
      • 2015-03-05
      • 2017-01-10
      • 1970-01-01
      • 2013-12-20
      • 2011-08-01
      • 2018-01-13
      • 2012-09-21
      • 1970-01-01
      • 2012-08-26
      相关资源
      最近更新 更多