5

First example

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

prints 22

Second example

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

prints 20

Question: Why in first example ++a in 3rd line also increments b, and why there is no such behavior in second example?

Update: New question arised.

Community
  • 1
  • 1
vladon
  • 8,158
  • 2
  • 47
  • 91

3 Answers3

10

Because pre-increment (++a) first increments the value of a, stores the result, and then returns the reference to a. Now a and b effectively point to the same object.

Post-increment (a++), however, first stores the current value of a in a temporary, increments a, and returns this temporary - to which your rvalue ref points. a and b point to different objects, more specifically - b is a temporary holding the value of a prior to incrementing.

This is the reason why it's encouraged to use ++it over it++ for iterators and other complex objects that define increment / decrement: the latter creates a temporary copy and thus may be slower.

Violet Giraffe
  • 32,368
  • 48
  • 194
  • 335
  • Is it UB to use `b` after `a++` in second example? – vladon Feb 10 '16 at 12:40
  • And why in first case there is no temporary? Why `auto && b = a++` is not an equivalent to `auto && b = a; a++`? – vladon Feb 10 '16 at 12:42
  • @vladon: I _think_ that the temporary will live until the reference to it goes out of scope, meaning it's not UB, but I'm not sure. – Violet Giraffe Feb 10 '16 at 12:43
  • @vladon: Say `a` was `0`. Then `a++` must return `0`, and `a` must become `1`. If it was implemented the way you suggested, you wouldn't get the `0` value. `a++` doesn't return `a`, it returns a **copy** of `a`. – Violet Giraffe Feb 10 '16 at 12:45
  • @vladon You shouldn't add a second question to your original. Mark an answer and ask a new question. – erip Feb 10 '16 at 12:47
  • (Cannot know why, but I cannot mention you in answer with at-sign...) Ok, why then `++a` returns not a copy of `a` (already incremented), but `a` itself? And I think it's UB to use ref to temporary returned by `a++`... – vladon Feb 10 '16 at 12:47
  • @erip Ok, I'll do it. – vladon Feb 10 '16 at 12:48
  • 1
    @vladon: because the standard says `++a` returns a reference :) Now, *why* it's designed this way is a different question. I think it's just both faster (no copying involved) and gives additional flexibility (you can keep chaining calls on the `++` result). – Violet Giraffe Feb 10 '16 at 12:50
  • 1
    @vladon: and the reason you can't mention me here, I think, is because I get notifications of any comments on my answer anyway. Mentioning me on my posts would be redundant. – Violet Giraffe Feb 10 '16 at 12:51
  • @erip: New question: http://stackoverflow.com/questions/35315826/can-i-use-rvalue-to-temporary-is-it-ub-or-not – vladon Feb 10 '16 at 12:52
3

The difference is that ++a is an lvalue, however a++ is not. This is specified by 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

and [expr.post.incr]/1:

[...] The result is a prvalue.


Now we consider auto && b = ++a; . ++a is an lvalue. auto&& is a forwarding reference. Forwarding references can actually bind to lvalues: the auto may itself deduce to a reference type. This code deduces to int &b = ++a;.

When a reference is bound to an lvalue of the same type, the reference binds directly, so b becomes another name for a.


In the second example, auto && b = a++;, a++ is a prvalue. This means it doesn't have an associated address and it's no longer any relation to the variable a. This line has the same behaviour as ++a; auto && b = (a + 0); would.

Firstly, since a++ is a prvalue, auto&& deduces to int&&. (i.e. auto deduces to int). When a reference of non-class type is bound to a prvalue, a temporary object is copy-initialized from the value. This object has its lifetime extended to match the reference.

So b in the second case is bound to a different object from a, a "temporary" int (which is not really so temporary, since it lasts as long as b does).

The reference binding rules are in [dcl.init.ref].

M.M
  • 138,810
  • 21
  • 208
  • 365
1

In the second case (post-increment) b actually references the temporary created for (a++), so the increments do not affect b.

Erik Alapää
  • 2,585
  • 1
  • 14
  • 25