1

Assume we have code like this in C or C++:

foo(bar());

What is the function execution order in this case?

  1. Is it required that bar() is called first, and foo() is called no earlier than bar() returns, in which case foo() is passed the return value of bar()?
  2. Is it permitted that the compiler reorders the above, namely that it calls foo() first, and delays calling bar() until in the code of foo() the value of foo()’s parameter is actually needed?

Case 2 could be useful for optimization should foo() be defined like this:

void foo(someType par) {
    if(someTest())
        baz1();
    else
        baz2(par);
}

In which case calling bar() could even be completely skipped if someTest() returns false.

However, case 2 would also make the programmer have to be more careful, as it could sometimes lead to subtle bugs (for example with recurrence).

6 Answers6

4

In C++, [intro.execution]:

When calling a function (whether or not the function is inline), 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.

bar() must be evaluated before the call to foo begins.

There was some change in C++17 that the expression a(b) now evaluates a before b whereas before they were unsequenced. In this case, the evaluation of foo doesn't do anything, it's just an identifier. But had we had foo()(bar()), then the call to foo() would be sequenced before the call to bar(), whereas prior to C++17, the two were unsequenced.

Barry
  • 247,587
  • 26
  • 487
  • 819
  • That would complete forbid inlining and full-code optimisation. If that is about C++, it is very different for C. – too honest for this site Mar 01 '17 at 20:07
  • @Olaf No it doesn't, it simply explains what the observable behavior is. The observable behavior is that `bar()` is evaluated first. – Barry Mar 01 '17 at 20:09
  • "is sequenced **before execution** of every expression or statement in the body of the called function." - that goes much further than observable behaviour. OB allows to reorder statements arbitrarily, as long the OB does not change. This line explicitly forbids it. It might be because of C++ having constructors/destructors, etc. But it is definitively way more restrictive than in C (the question is unfortunately tagged both). – too honest for this site Mar 01 '17 at 20:11
  • @Olaf: But 1.9 [intro.execution] says "[conforming implemenations] need not copy or emulate the structure of the abstract machine. Rather, conforming implementations are required to emulate (only) the observable behavior of the abstract machine as explained below." That applies also to "sequenced before"; only observable behaviour is required to happen in order. – rici Mar 01 '17 at 20:23
  • @rici: If you refer to the C++ standard: I'm not really into it (I'm a C person). If that is so, it should be part of the answer as it relaxes requirements indeed. I just wanted to point out from the text shown, it would be more strict than in C. – too honest for this site Mar 01 '17 at 20:25
  • The citation is not related to when `a` and `b` are evaluated relative to each other. `a` is not the body of the function, but the expression yielding a function. The body is the code at the function definition. – too honest for this site Mar 01 '17 at 20:28
  • @Olaf: I'm fully aware of your Cness :) and indeed it is from the C++ standard (I should have written 1.9.1, though). But it's essentially the same definition of "sequenced before" as provided by the C standard (although the sequenced-before *rules* are different); in both cases, it only applies to observed behaviour. (It's also possible that C++ and C can observe different behaviours, but the clauses look very similar to me.) – rici Mar 01 '17 at 20:37
  • @Olaf: `a` **is** the "postfix expression designating the called function" – rici Mar 01 '17 at 20:37
  • @riciYes, I got that from your comment, but it cannot be concluded from the answer, which makes my comment relevant. Not sure, but doesn't C++17 (at least) support defered evaluation? That would raise the question to another level of too broad, I suspect. – too honest for this site Mar 01 '17 at 20:43
  • "... postfix expression designating ..." - Whatever, I did not recall the exact term. Point is it is definitively not the function body the answer refers to. – too honest for this site Mar 01 '17 at 20:44
2
  1. Is it required that bar() is called first […]?

The code is required to behave as if that's what's happening.

  1. Is it permitted that the compiler reorders the above […]?

Yes, if the observable behavior is the same as if bar() was evaluated first.

This is known as the "as-if" rule:

[…] an implementation is free to disregard any requirement of this International Standard as long as the result is as if the requirement had been obeyed, as far as can be determined from the observable behavior of the program.

Community
  • 1
  • 1
emlai
  • 37,861
  • 9
  • 87
  • 140
  • Are you sure? [expr.call] states *The evaluations of the postfix expression and of the arguments are all unsequenced relative to one another. All side effects of argument evaluations are sequenced before the function is entered* – NathanOliver Mar 01 '17 at 20:09
  • @NathanOliver Yeah, the standard quotes in the linked Q&A seem to support this. – emlai Mar 01 '17 at 20:12
1

In addition to what others have said, you might consider

  foo( bar1(), bar2() );

which is a more interesting case. Here, the order is allowed to be [bar1, bar2, foo] or [bar2, bar1, foo].

FredK
  • 4,036
  • 1
  • 6
  • 11
0

There is a sequence point after function argument evaluation (i.e. before the code of any function is entered). Hence, bar() must be evaluated before foo(...) will be entered. Confer, for example, the C11 standard:

6.5.2.2 Function calls: (10) There is a sequence point after the evaluations of the function designator and the actual arguments but before the actual call. Every evaluation in the calling function (including other function calls) that is not otherwise specifically sequenced before or after the execution of the body of the called function is indeterminately sequenced with respect to the execution of the called function.

Stephan Lechner
  • 33,675
  • 4
  • 27
  • 49
0

If you have the following statement

foo(bar());

The function bar() will be called before foo(). The compiler cannot reorder these function calls.

Cory Kramer
  • 98,167
  • 13
  • 130
  • 181
0

It's same as :

type bar_result = bar();
foo(bar_result);
  1. bar() will be called first.
  2. bar_result is passed as a copy/referencie.
J.Guarin
  • 91
  • 1
  • 3