10

In the code below is it required that f1 be called before f2 (or vice-versa) or is it unspecified?

int f1();
int f2();

std::initializer_list<int> list { f1(), f2() };
Michael Anderson
  • 70,661
  • 7
  • 134
  • 187
linus linus
  • 161
  • 4
  • 1
    `f1` will be called first. FWIW, this is a question on cppquiz.org. You'd probably find the relevant standard piece in the answer on there. – chris Jul 10 '15 at 06:25
  • See also [Are multiple mutations within initializer lists undefined behavior?](http://stackoverflow.com/q/14442894/1708801) and [Are multiple mutations of the same variable within initializer lists undefined behavior pre C++11](http://stackoverflow.com/q/19881803/1708801) – Shafik Yaghmour Jul 10 '15 at 09:27

1 Answers1

11

This is one interesting corner of the C++ standard where execution order is well defined. Section 8.5.4 [dcl.init.list], paragraph 4:

Within the initializer-list of a braced-init-list, the initializer-clauses, including any that result from pack expansions (14.5.3), are evaluated in the order in which they appear. That is, every value computation and side effect associated with a given initializer-clause is sequenced before every value computation and side effect associated with any initializer-clause that follows it in the comma-separated list of the initializer-list.

So in the initializer list, the function calls are evaluated left-to-right.

rici
  • 234,347
  • 28
  • 237
  • 341
  • 1
    Just for completeness its probably worth noting that this is in direct contrast to `h(f1(),f2())` where the order is unspecified, (and then theres the even more error inducing potential orders for `h(f1(), f2(new X()))` where `new X()` may be evaluated, then `f1()` called and an exception raised, so the X is leaked...) – Michael Anderson Jul 10 '15 at 06:31
  • 1
    @MichaelAnderson: Well, it's true. It only applies to *initialization lists*. But that's what the question is about. I did try to imply that the behaviour is unusually well-specified. (Evaluation order is also specified for the builtin comma operator, but not, as you note, for the arguments in a function call.) – rici Jul 10 '15 at 06:37
  • 1
    I was thinking of the difference between pre C++11 where I might have called a function taking multiple `int`s using vargs or overloading like `fn(f1(),f2(),f3())` and need to be wary of order, but in C++11 I can use initializer lists `fn( { f1(), f2(), f3() } )` and the order is guaranteed. But of course initialization lists have more uses than just that. – Michael Anderson Jul 10 '15 at 06:51