为什么这两个右值引用示例具有不同的行为?

Why this two rvalue references examples have different behavior?

第一个例子

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

打印22

第二个例子

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

打印20

问题: 为什么在第一个例子中第 3 行的 ++a 也递增 b,为什么在第二个例子中没有这样的行为?

更新: 出现。

因为pre-increment(++a)先自增a的值,存储结果,然后然后 returns引用至 a。现在 ab 实际上指向同一个对象。

Post-increment(a++),然而,先把a的当前值存储在一个临时的,递增a,然后returns这个临时的 - 你的右值 ref 指向它。 ab 指向不同的对象,更具体地说 - b 是在递增之前临时保存 a 的值。

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

在第二种情况下(post-增量)b实际上引用了为(a++)创建的临时文件,所以增量不影响b。

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

指定的

The operand of prefix ++ is modified by adding 1 [...] The operand shall be a modifiable lvalue. [...] The result is the updated operand; it is an lvalue

和[expr.post.incr]/1:

[...] The result is a prvalue.


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

当引用绑定到相同类型的左值时,引用直接绑定,所以b成为a的别称。


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

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

所以在第二种情况下 b 绑定到与 a 不同的对象,一个 "temporary" int(这不是真的那么临时,因为它持续到 b 确实如此)。

参考绑定规则在[dcl.init.ref].