8

I tried constructing pairs of integers where the second integer is greater than the first by 1:

1 2
2 3
3 4

Using both std::make_pair and the constructor like so:

std::make_pair(n, n++);

However, this results in the pairs being in reverse:

2 1
3 2
4 3

If I was to put the post-increment on the first parameter or use (n+1) instead, I get the desired result.

Why does it behave this way?

JeJo
  • 20,530
  • 5
  • 29
  • 68
Pesho_T
  • 658
  • 1
  • 4
  • 15
  • https://stackoverflow.com/questions/2934904/order-of-evaluation-in-c-function-parameters https://stackoverflow.com/questions/38501587/what-are-the-evaluation-order-guarantees-introduced-by-c17 – Mat May 02 '20 at 22:11
  • @Mat if evaluated left to right, the result would be the same as shown in the question (see yakk's answer) – M.M May 03 '20 at 08:01

2 Answers2

6

This has to do with the order of evaluation. From cppreference.com

Order of evaluation of any part of any expression, including order of evaluation of function arguments is unspecified. The compiler can evaluate operands and other subexpressions in any order, and may choose another order when the same expression is evaluated again.

Therefore, one can not guarantee whether n or n++ evaluates first.

Ted Lyngmo
  • 37,764
  • 5
  • 23
  • 50
JeJo
  • 20,530
  • 5
  • 29
  • 68
  • Close, but not quite. At least in C++14 – Yakk - Adam Nevraumont May 02 '20 at 22:24
  • Signature of make_pair – Yakk - Adam Nevraumont May 02 '20 at 22:27
  • 2
    Sorry, small child jumping on me. `make_pair(int&, int&&)` -- the second argument is an rvalue, so you get a copy; the first argument is a reference. So the real question is, how are the side effects of parameter evaluation sequenced relative to the function call; how they are sequenced relative to each other matters far less. Are the side effects of `n++` evaluated before, after, or not sequenced relative to the function call? That is slightly different question than your answer answers. – Yakk - Adam Nevraumont May 02 '20 at 22:39
6

From cppreference:

When calling a function (whether or not the function is inline, and whether or not explicit function call syntax is used), every value computation and side effect associated with any argument expression, or with the postfix expression designating the called function, is sequenced before execution of every expression or statement in the body of the called function.

So what is going on here is this.

int n = 0;
auto p = std::make_pair( n, n++ );

First we determine the overload of make_pair; we get:

make_pair<int&, int>( int&, int&& )

ie, the first argument is a rvalue reference (which ends up binding to n), the second in an lvalue reference (which ends up binding to the temporary that n++ returns).

We evaluate the arguments of make_pair. They are sequenced in an arbitrary manner, but you'll see it doesn't matter here.

Binding n to int& doesn't copy a value, it just stores a reference.

Binding n++ to int&& creates a temporary object, copies the value of n into it, then sets up a side effect to increase n afterwards.

When the side effect happens is the key here. As noted above, it has to happen sometime before the function make_pair is called.

It could happen before n is evaluated for the first argument, or after; it doesn't matter, because we are binding a reference to n to the argument. It is, however, evaluated before we do the body of make_pair.

So within make_pair, it is guaranteed to have a reference to n, whose value is 1, and a reference to a temporary whose value is 0. It then runs and returns a pair with those values.


It appears you misunderstood what n++ means -- it means "return the value of n, then afterwards increase it", it does not mean "return a value 1 bigger than n".

The way you return a value 1 bigger than n is n+1.

Yakk - Adam Nevraumont
  • 235,777
  • 25
  • 285
  • 465
  • 3
    So `std::make_pair(n++, n)` would guarantee the result OP wants? – Ted Lyngmo May 02 '20 at 22:58
  • Thanks, that's a great explanation. I didn't consider the lvalue/rvalue references. And the post vs pre increment is a silly mistake I didn't notice due to the confusion over the wrong order. If I had it right I'd have been even more confused due to getting a pair of the same integers. – Pesho_T May 02 '20 at 23:41
  • @TedLyngmo It would appear so, however readability is significantly impacted. Anyone not familiar with the concepts in this answer would be left scratching their head – Pesho_T May 02 '20 at 23:46
  • @Pesho_T I'm actually not sure if it's guaranteed but with what Yakk explained in mind, it feels like it should be. For me it increases readability. I would have chosen that order anyway - but for the wrong reason.:-) – Ted Lyngmo May 02 '20 at 23:55