c++language-lawyerc++23sequence-points

In C++ are all subexpressions of function call arguments sequenced consistently?


Consider a situation where we have the following functions:

// A function with two parameters.
void AcceptTwoInts(int, int);

// Two functions that accept an integer and return another integer.
int Foo(int);
int Bar(int);

// Two functions that produce some integer.
int Baz();
int Qux();

Further, consider this function call expression:

AcceptTwoInts(Foo(Baz()), Bar(Qux()));

My question: Is the call to Baz sequenced (even indeterminately) with respect to the call to Qux? I'm wondering if I can assume anything about whether all subexpressions for one argument have been evaluated by the time we start evaluating subexpressions for another argument, regardless of which order the compiler chooses for those two arguments.

(I don't care about the "as if" rule here; I'm talking about situations where I can tell the difference.)


The reason I wonder if there might be some guarantee is this sentence in [expr.call]/7:

The initialization of a parameter, including every associated value computation and side effect, is indeterminately sequenced with respect to that of any other parameter.

The wording seems ambiguous here; it seems to me it can be interpreted in two ways:

  1. The way that gives me a guarantee: given arguments A and B, either all subexpressions associated with A are sequenced before all subexpressions associated with B or vice versa.

  2. The way that gives me no guarantee: just the initialization step for A and the initialization step for B are indeterminately sequenced, not any of the subexpressions that go into that initialization.

Which one is the correct interpretation? In other words, is the call to Baz a "value computation" with respect to initializing first parameter for AcceptTwoInts?


Solution

  • This was clarified by CWG2599. In C++23, [dcl.init.general]/19 reads:

    Initialization includes the evaluation of all subexpressions of each initializer-clause of the initializer (possibly nested within braced-init-lists) and the creation of any temporary objects for function arguments or return values.

    Thus the call to Baz is indeed indeterminately sequenced with the call to Qux.