114

If we have three functions (foo, bar, and baz) that are composed like so...

foo(bar(), baz())

Is there any guarantee by the C++ standard that bar will be evaluated before baz?

Clark Gaebel
  • 17,280
  • 20
  • 66
  • 93

6 Answers6

122

No, there's no such guarantee. It's unspecified according to the C++ standard.

Bjarne Stroustrup also says it explicitly in "The C++ Programming Language" 3rd edition section 6.2.2, with some reasoning:

Better code can be generated in the absence of restrictions on expression evaluation order

Although technically this refers to an earlier part of the same section which says that the order of evaluation of parts of an expression is also unspecified, i.e.

int x = f(2) + g(3);   // unspecified whether f() or g() is called first
melpomene
  • 84,125
  • 8
  • 85
  • 148
Eli Bendersky
  • 263,248
  • 89
  • 350
  • 412
  • 9
    Yes, but better code could be WRITTEN (=cleaner) if expression evaluation order was STRICT, which is generally a lot more important than code generation. See this example: http://stackoverflow.com/questions/43612592/the-case-where-c-ab-is-not-equal-to-c-ab-b-b1?noredirect=1#comment74274141_43612592 So there, Stroustrup. – Bill Kotsias Apr 25 '17 at 13:57
  • 5
    If ordering matters you are free to do the sequencing yourself. To do otherwise would always incur a cost for something that doesn't always (rarely?) matters. I think the policy of not paying for what you don't use is the only thing most C++ programmers agree on. – tweej Dec 02 '17 at 09:53
  • 4
    Shouldn't it be "unspecified behavior" instead of "undefined"? – GoodDeeds Jan 06 '18 at 19:38
  • 1
    @GoodDeeds Prior C++17, undefined behaviour if the functions causes side effects on the same memory location. Post C++17 it is unspecified. – Passer By Jun 22 '18 at 16:53
  • 1
    It's not undefined, its implementation defined/unspecified. That is, the code in the question can't cause abritrary unexpected behavior -- it *MUST* either run bar() then run baz() OR run baz() then run bar(). Nothing else is legal (it can't interleave the operations in the two functions if doing so would be visible). – Chris Dodd Jul 19 '18 at 20:37
  • 7
    @ChrisDodd downvoting an accepted answer due to using the word "undefined" vs. "unspecified" feels like malicious pedantry to me... I didn't say this is "undefined behavior", and otherwise "undefined" and "unspecified" seem synonymous? In any case, proposing an edit to the answer would have been a more productive way to discuss this – Eli Bendersky Jul 20 '18 at 13:19
  • `string Config::value(string sKey, bool bReload)`. `void foo(Config::value("a", true), Config::value("b"), false);` Using 2 true would lost be wasteful, using 1 true doesn't guarantee the other one also come from a reloaded config. – Zhang Jan 10 '20 at 06:47
  • Since left-to-right order _is_ guaranteed for _braced-init-lists_, `std::apply(foo, std::tuple{bar(), baz()})` would be save to use – h4ssi Mar 28 '22 at 08:49
27

From [5.2.2] Function call,

The order of evaluation of arguments is unspecified. All side effects of argument expression evaluations take effect before the function is entered.

Therefore, there is no guarantee that bar() will run before baz(), only that bar() and baz() will be called before foo.

Also note from [5] Expressions that:

except where noted [e.g. special rules for && and ||], the order of evaluation of operands of individual operators and subexpressions of individual expressions, and the order in which side effects take place, is unspecified.

so even if you were asking whether bar() will run before baz() in foo(bar() + baz()), the order is still unspecified.

Daniel Trebbien
  • 38,421
  • 18
  • 121
  • 193
  • 4
    An example of a "special note" from [5.14] Logical AND operator: "Unlike `&`, `&&` guarantees left-to-right evaluation: the second operand is not evaluated if the first operand is `false`." – Daniel Trebbien May 29 '10 at 12:31
21

There's no specified order for bar() and baz() - the only thing the Standard says is that they will both be evaluated before foo() is called. From the C++ Standard, section 5.2.2/8:

The order of evaluation of arguments is unspecified.

  • 5
    The fact that they are evaluated before foo() is a bit reassuring, at least. – Bill Kotsias Apr 25 '17 at 13:58
  • 2
    @BillKotsias The standard also says function calls cannot overlap (i.e. an implementation can't run line 1 of `bar`, then line 1 of `baz`, then line 2 of `bar`, etc.), which is also nice. :-) – melpomene Jul 31 '19 at 21:04
17

C++17 specifies evaluation order for operators that was unspecified until C++17. See the question What are the evaluation order guarantees introduced by C++17? But note your expression

foo(bar(), baz())

has still unspecified evaluation order.

273K
  • 29,503
  • 10
  • 41
  • 64
3

In C++11, the relevant text can be found in 8.3.6 Default arguments/9 (Emphasis mine)

Default arguments are evaluated each time the function is called. The order of evaluation of function arguments is unspecified. Consequently, parameters of a function shall not be used in a default argument, even if they are not evaluated.

The same verbiage is used by C++14 standard as well, and is found under the same section.

R Sahu
  • 204,454
  • 14
  • 159
  • 270
-1

As others have already pointed out, the standard does not give any guidance on order of evaluation for this particular scenario. This order of evaluation is then left to the compiler, and the compiler might have a guarantee.

It's important to remember that the C++ standard is really a language to instruct a compiler on constructing assembly/machine code. The standard is only one part of the equation. Where the standard is ambiguous or is specifically implementation defined you should turn to the compiler and understand how it translates C++ instructions into true machine language.

So, if order of evaluation is a requirement, or at least important, and being cross-compiler compatible is not a requirement, investigate how your compiler will ultimately piece this together, your answer could ultimate lie there. Note that the compiler could change it's methodology in the future