35

I've looked at a bunch of questions regarding sequence points, and haven't been able to figure out if the order of evaluation for x*f(x) is guaranteed if f modifies x, and is this different for f(x)*x.

Consider this code:

#include <iostream>

int fx(int &x) {
  x = x + 1;
  return x;
}

int f1(int &x) {
  return fx(x)*x; // Line A
}

int f2(int &x) {
  return x*fx(x); // Line B
}

int main(void) {
  int a = 6, b = 6;
  std::cout << f1(a) << " " << f2(b) << std::endl;
}

This prints 49 42 on g++ 4.8.4 (Ubuntu 14.04).

I'm wondering whether this is guaranteed behavior or unspecified.

Specifically, in this program, fx gets called twice, with x=6 both times, and returns 7 both times. The difference is that Line A computes 7*7 (taking the value of x after fx returns) while Line B computes 6*7 (taking the value of x before fx returns).

Is this guaranteed behavior? If yes, what part of the standard specifies this?

Also: If I change all the functions to use int *x instead of int &x and make corresponding changes to places they're called from, I get C code which has the same issues. Is the answer any different for C?

Navin
  • 1,401
  • 10
  • 16
  • Note that sequence points are part of the C language standard's treatment of order of evaluation. They're not used in the C++ standard, so I'll remove the tag unless anyone strongly objects. – Useless Sep 10 '15 at 14:45
  • 1
    Technically, sequence points were used in C++ before C++11, were it was replaced by "sequenced before / after" and "unsequenced / indeterminaly sequenced". The code in this question invokes UB. See [this explanation on sequence points and co](http://stackoverflow.com/questions/4176328/undefined-behavior-and-sequence-points) – Synxis Sep 10 '15 at 17:29
  • 1
    @Useless, as @Synxis pointed out, pre-C++11 versions of C++ do have sequence points. Also, the same question exists for C (if I just convert all the functions to use `int *x` instead of `int &x` and make corresponding changes to places they're called from. – Navin Sep 10 '15 at 23:15
  • If you want to know about sequence points, you're probably referring to C++03? If so, I think adding the [c++03] tag might be useful; there are already many questions which imply c++ >= 2011. – dyp Sep 10 '15 at 23:40
  • @Useless C11 does not have sequence points either – M.M Sep 11 '15 at 00:19
  • 2
    This is either unspecified or undefined. I wonder in what kind of real programming this distinction matters. – n. m. could be an AI Sep 11 '15 at 06:23
  • @n.m. I think this is fairly common, e.g. `cout << foo();` . Some are apparently arguing that if `foo()` contained a `cout << ` statement then it is undefined behaviour. – M.M Sep 11 '15 at 06:54
  • Perhaps it would be a wiser thought to entirely move away from particular programming languages (in the first place at least), and to reask this question in SE's mathematics network, because: once you understand the mathematical background of things, you will most likely be able to transform the facts you've learned onto the various languages you're programming in. – syntaxerror Sep 11 '15 at 06:58
  • @M.M. Not exactly the same case. `operator<<` gets a reference. Side effects on `cout` don't change references to `cout`. – n. m. could be an AI Sep 11 '15 at 08:52
  • @n.m. outputting to `cout` no doubt changes some variables in the `istream` class or underlying stream... if not then replace `cout` with someone's custom log class – M.M Sep 11 '15 at 10:13
  • @M.M. These changes are sequenced. You would need to use a custom log class and a custom `<<` operator that takes it *by value*, and a function that takes it *by reference*. Pretty rare if you ask me. – n. m. could be an AI Sep 11 '15 at 10:26
  • @n.m. Yes, I'm saying they are sequenced; the current top answer says that code in different functions may be interleaved. (Or it did say that until recently, now it seems unclear what it is saying) – M.M Sep 11 '15 at 10:46

6 Answers6

22

In terms of evaluation sequence, it is easier to think of x*f(x) as if it was:

operator*(x, f(x));

so that there are no mathematical preconceptions on how multiplication is supposed to work.

As @dan04 helpfully pointed out, the standard says:

Section 1.9.15: “Except where noted, evaluations of operands of individual operators and of subexpressions of individual expressions are unsequenced.”

This means that the compiler is free to evaluate these arguments in any order, the sequence point being operator* call. The only guarantee is that before the operator* is called, both arguments have to be evaluated.

In your example, conceptually, you could be certain that at least one of the arguments will be 7, but you cannot be certain that both of them will. To me, this would be enough to label this behaviour as undefined; however, @user2079303 answer explains well why it is not technically the case.

Regardless of whether the behaviour is undefined or indeterminate, you cannot use such an expression in a well-behaved program.

Maksim Solovjov
  • 3,147
  • 18
  • 28
  • 2
    Are you sure about this? The *associativity* of multiplication is from left to right. I don't think the compiler is allowed to evaluate the arguments in any order. However, it might, I think, evaluate f(x) prior to the multiplication. As such the result is unspecified. – Bathsheba Sep 10 '15 at 14:33
  • 2
    There is a *draft* of the C++ standard available at http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3242.pdf . Section 1.9.15 states “Except where noted, evaluations of operands of individual operators and of subexpressions of individual expressions are unsequenced.” – dan04 Sep 10 '15 at 14:34
  • 14
    @Bathsheba: Associativity is only about how the operator associates: it has nothing to do with the order you determine what the arguments actually are. –  Sep 10 '15 at 14:35
  • 10
    @Bathsheba: What left-to-right associativity means is that `a * b * c` is defined as equivalent to `(a * b) * c` rather than `a * (b * c)`, but the compiler is still free to evaluate sub-expression `c` before `a` or `b`. – dan04 Sep 10 '15 at 14:36
  • 1
    This answer can be improved: the code invokes undefined behavior, so there is no formal guarantee whether one of the arguments will be 7. – Synxis Sep 10 '15 at 17:32
  • I'd say this would be stretching it. The order of execution is left for the implementation, but the standard guarantees that by the time `operator*` is called, both operands have been calculated. So there is a defined finite set of possible operand values: `(6, 7), (7, 7)`. – Maksim Solovjov Sep 10 '15 at 18:07
  • I'd be happy to include the UB point in the answer; I'm just not 100% sure this falls under it, with full nasal demon implications... – Maksim Solovjov Sep 10 '15 at 18:12
  • 1
    Literally: `(§1.9/15) If a side effect on a scalar object is unsequenced relative to [...] a value computation using the value of the same scalar object, the behavior is undefined.`. The side effect here is `fx(x)`, and the value used is obviously `x`. It is explained in the second answer to the question I linked in a previous comment – Synxis Sep 10 '15 at 18:14
  • Heh, missed it. Thank you – Maksim Solovjov Sep 10 '15 at 18:14
  • @Synxis: Why is not the evaluation of the first operand of `*` (i.e. `x`) not required to entirely precede or entirely follow the execution of the function `f(x)`? A compiler would be within its rights to evaluate the first operand of `*` before invoking `f(x)` when the moon was full, and execute `f(x)` first the rest of the time, but I see no reason it wouldn't be required to do one or the other. – supercat Sep 10 '15 at 21:28
  • The result would only be undefined if at least one of the allowed evaluation-orders involved UB. As-is, you only get unspecified behavior, as the `x` which isn't fed through `f` can be read before `f` is called or after it returns. – Deduplicator Sep 10 '15 at 22:24
  • 2
    @Synxis: You missed something though: The call of `f` cannot overlap with reading `x` for the other term, it's indeterminately sequenced (meaning before or after, unspecified). Thus, we don't get UB. – Deduplicator Sep 10 '15 at 22:29
  • 1
    Integer multiplication **is not a function call**. The rules for function calls therefore do not apply. IMO writing `x * f(x)` as `operator*(x, f(x))` is misleading, if there's no class type involved. – dyp Sep 10 '15 at 22:32
  • @dyp: The call to `f(x)` however is a function call. Whether or not the operator `*` is evaluated as a function call would only be relevant if it had side-effects, which it doesn't. The call to `f(x)` may occur before the left-hand operand of `*` is evaluated, or it may occur after, and a compiler need not be consistent about when it occurs, but the compiler must use one of those two orderings. – supercat Sep 10 '15 at 23:22
  • @supercat My remark about `*` not being a function call was referring to the *rationale*. We cannot use any rationale about the evaluation of *arguments of a function call* if there is no function call (for the multiplication). Btw, to make the evaluations *indeterminately sequenced* as you seem to suggest, instead of *unsequenced*, one needs to additional guarantee of [intro.execution]p15 "Every evaluation in the calling function [...]" which has not been quoted yet. – dyp Sep 10 '15 at 23:38
  • @dyp: If your point was that people were quoting the wrong section, I'll agree with that. The conclusion is correct, however. BTW, in what cases do operators without side-effects, other than `,`, `&&`, and `||`, have semantics different from function calls? – supercat Sep 10 '15 at 23:42
  • @supercat "semantics different from function calls" Are you referring to *sequencing*? In any case, you probably forgot to exclude `?:` ;) – dyp Sep 10 '15 at 23:48
  • @dyp: Yeah, I did forget `?:`. And I mean including sequencing, but I'm also interested in a broader sense, though I guess I also forgot to include the effects of combining the dereferencing and address-of operators. – supercat Sep 10 '15 at 23:56
  • 1
    This answer is wrong and there is no undefined behaviour. I guess the upvotes came before you edited in the parts claiming undefined behaviour. – M.M Sep 11 '15 at 00:20
  • @M.M, "If a side effect on a scalar object is unsequenced relative to [...] a value computation using the value of the same scalar object, the behavior is undefined.". `f(x)` introduces a side effect on a scalar object `x`, and `x` is a value computation using the same object. By definition then, this behaviour is undefined – Maksim Solovjov Sep 11 '15 at 05:45
  • 1
    @MaksimSolovjov The side effect of `f(x)` is to call a function. There is no side-effect on `x`. Statements in a function are not sub-expressions of a function call. Side-effects of statements in the function are not side-effects of the function call. They are side-effects which occur after the function call has begun and before the function call completes – M.M Sep 11 '15 at 05:47
  • And what if `f(x)` gets inlined? – Maksim Solovjov Sep 11 '15 at 05:48
  • 2
    Inlining doesn't change the definition of a program's sequence. It's the compiler's job to make sure it doesn't break well-formed code when inlining a function. Optimization may change the *unspecified* effects, but if there was no undefined behaviour before, then the compiler can't introduce undefined behaviour. See my answer for a detailed writeup if you haven't already :) – M.M Sep 11 '15 at 05:49
  • I can see where you are coming from, but I think there is a wider discussion to be had on this – Maksim Solovjov Sep 11 '15 at 05:50
  • @M.M, the standard considers a function call that has side effects to be a side effect too. – Maksim Solovjov Sep 11 '15 at 06:00
  • @MaksimSolovjov Please provide a standard quote to support that assertion. – M.M Sep 11 '15 at 06:01
  • I included the quote in the answer: 1.9.12 – Maksim Solovjov Sep 11 '15 at 06:03
  • 1
    The *function call* is a side-effect. **Not** the expressions within the function being called. What you're suggesting is absolutely preposterous and would mean that just about any complicated program ever is broken. – M.M Sep 11 '15 at 06:06
  • Also, this side-effect discussion is irrelevant. As you already quoted yourself, "If a side effect on a scalar object is unsequenced relative to [...] a value computation using the value of the same scalar object, the behavior is undefined.”" However the relationship in question is actually *indeterminately sequenced*. My answer provides quotes clearly proving the relationship is indeterminately sequenced. – M.M Sep 11 '15 at 06:09
12

The evaluation order of arguments is not specified by the standard, so the behaviour that you see is not guaranteed.

Since you mention sequence points, I'll consider the c++03 standard which uses that term while the later standards have changed wording and abandoned the term.

ISO/IEC 14882:2003(E) §5 /4:

Except where noted, 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...


There is also discussion on whether this is undefined behaviour or is the order merely unspecified. The rest of that paragraph sheds some light (or doubt) on that.

ISO/IEC 14882:2003(E) §5 /4:

... Between the previous and next sequence point a scalar object shall have its stored value modified at most once by the evaluation of an expression. Furthermore, the prior value shall be accessed only to determine the value to be stored. The requirements of this paragraph shall be met for each allowable ordering of the subexpressions of a full expression; otherwise the behavior is undefined.

x is indeed modified in f and it's value is read as an operand in the same expression where f is called. And it's not specified whether x reads the modified or non-modified value. That might scream Undefined Behaviour! to you, but hold your horses, because the standard also states:

ISO/IEC 14882:2003(E) §1.9 /17:

... When calling a function (whether or not the function is inline), there is a sequence point after the evaluation of all function arguments (if any) which takes place before execution of any expressions or statements in the function body. There is also a sequence point after the copying of a returned value and before the execution of any expressions outside the function 11) ...

So, if f(x) is evaluated first, then there is a sequence point after copying the returned value. So the above rule about UB does not apply because the read of x is not between the next and previous sequence point. The x operand will have the modified value.

If x is evaluated first, then there is a sequence point after evaluating the arguments of f(x) Again, the rule about UB does not apply. In this case x operand will have the non-modified value.

In summary, the order is unspecified but there is no undefined behaviour. It's a bug, but the outcome is predictable to some degree. The behaviour is the same in the later standards, even though the wording changed. I'll not delve into those since it's already covered well in other good answers.


Since you ask about similar situation in C

C89 (draft) 3.3/3:

Except as indicated by the syntax 27 or otherwise specified later (for the function-call operator () , && , || , ?: , and comma operators), the order of evaluation of subexpressions and the order in which side effects take place are both unspecified.

The function call exception is already mentioned here. Following is the paragraph that implies the undefined behaviour if there were no sequence points:

C89 (draft) 3.3/2:

Between the previous and next sequence point an object shall have its stored value modified at most once by the evaluation of an expression. Furthermore, the prior value shall be accessed only to determine the value to be stored.26

And here are the sequence points defined:

C89 (draft) A.2

The following are the sequence points described in 2.1.2.3

  • The call to a function, after the arguments have been evaluated (3.3.2.2).

  • ...

  • ... the expression in a return statement (3.6.6.4).

The conclusions are the same as in C++.

Community
  • 1
  • 1
eerorika
  • 232,697
  • 12
  • 197
  • 326
7

A quick note on something I don't see covered explicitly by the other answers:

if the order of evaluation for x*f(x) is guaranteed if f modifies x, and is this different for f(x)*x.

Consider, as in Maksim's answer

operator*(x, f(x));

now there are only two ways of evaluating both arguments before the call as required:

auto lhs = x;        // or auto rhs = f(x);
auto rhs = f(x);     // or auto lhs = x;
    return lhs * rhs

So, when you ask

I'm wondering whether this is guaranteed behavior or unspecified.

the standard doesn't specify which of those two behaviours the compiler must choose, but it does specify those are the only valid behaviours.

So, it's neither guaranteed nor entirely unspecified.


Oh, and:

I've looked at a bunch of questions regarding sequence points, and haven't been able to figure out if the order of evaluation ...

sequence points are a used in the C language standard's treatment of this, but not in the C++ standard.

Useless
  • 64,155
  • 6
  • 88
  • 132
  • 4
    The phrase "sequence point" may have been removed from the C++11 and later standards, but it *was* used in previous C++ standards ( [ref](https://isocpp.org/wiki/faq/misc-technical-issues#sequence-points) ). – R.M. Sep 10 '15 at 15:35
  • Agreed, but there's no resisting the ineluctable march of progress. – Useless Sep 10 '15 at 16:02
  • I was starting to despair, as everybody seemed to avoid saying what's guaranteed, what isn't, and how that's called. Good answer. – Deduplicator Sep 10 '15 at 22:34
  • *unspecified* is a technical term which means that it is not Undefined Behaviour, but there is more than one permitted result. So it is definitely unspecified. – M.M Sep 11 '15 at 00:21
6

In the expression x * y, the terms x and y are unsequenced. This is one of the three possible sequencing relations, which are:

  • A sequenced-before B: A must be evaluated, with all side-effects complete, before B begins evaluationg
  • A and B indeterminately-sequenced: one of the two following cases is true: A is sequenced-before B, or B is sequenced-before A. It is unspecified which of those two cases holds.
  • A and B unsequenced: There is no sequencing relation defined between A and B.

It is important to note that these are pair-wise relations. We cannot say "x is unsequenced". We can only say that two operations are unsequenced with respect to each other.

Also important is that these relations are transitive; and the latter two relations are symmetric.


unspecified is a technical term which means that the Standard specifies a set number of possible results. This is different to undefined behaviour which means that the Standard does not cover the behaviour at all. See here for further reading.


Moving onto the code x * f(x). This is identical to f(x) * x, because as discussed above, x and f(x) are unsequenced, with respect to each other, in both cases.

Now we come to the point where several people seem to be coming unstuck. Evaluating the expression f(x) is unsequenced with respect to x. However, it does not follow that any statements inside the function body of f are also unsequenced with respect to x. In fact, there are sequencing relations surrounding any function call, and those relations cannot be ignored.

Here is the text from C++14:

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. [Note: Value computations and side effects associated with different argument expressions are unsequenced. —end note ] 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.

with footnote:

In other words, function executions do not interleave with each other.

The bolded text clearly states that for the two expressions:

  • A: x = x + 1; inside f(x)
  • B: evaluating the first x in the expression x * f(x)

their relationship is: indeterminately sequenced.

The text regarding undefined behaviour and sequencing is:

If a side effect on a scalar object is unsequenced relative to either another side effect on the same scalar object or a value computation using the value of the same scalar object, and they are not potentially concurrent (1.10), the behavior is undefined.

In this case, the relation is indeterminately sequenced, not unsequenced. So there is no undefined behaviour.

The result is instead unspecified according to whether x is sequenced before x = x + 1 or the other way around. So there are only two possible outcomes, 42 and 49.


In case anyone had qualms about the x in f(x), the following text applies:

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.

So the evaluation of that x is sequenced before x = x + 1. This is an example of an evlauation that falls under the case of "specifically sequenced before" in the bolded quote above.


Footnote: the behaviour was exactly the same in C++03, but the terminology was different. In C++03 we say that there is a sequence point upon entry and exit of every function call, therefore the write to x inside the function is separated from the read of x outside the function by at least one sequence point.

Community
  • 1
  • 1
M.M
  • 138,810
  • 21
  • 208
  • 365
4

You need to distinguish:

a) Operator precedence and associativity, which controls the order in which the values of subexpressions are combined by their operators.

b) The sequence of subexpression evaluation. E.g. in the expression f(x)/g(x), the compiler can evaluate g(x) first and f(x) afterwards. Nonetheless, the resulting value must be computed by dividing respective sub-values in the right order, of course.

c) The sequence of side-effects of the subexpressions. Roughly speaking, for example, the compiler might, for sake of optimization, decide to write values to the affected variables only at the end of the expression or any other suitable place.

As a very rough approximation, you can say, that within a single expression, the order of evaluation (not associativity etc.) is more or less unspecified. If you need a specific order of evaluation, break down the expression into series of statements like this:

int a = f(x); int b = g(x); return a/b;

instead of

return f(x)/g(x);

For exact rules, see http://en.cppreference.com/w/cpp/language/eval_order

Useless
  • 64,155
  • 6
  • 88
  • 132
Jaromír Adamec
  • 579
  • 4
  • 13
1

Order of evaluation of the operands of almost all C++ operators is unspecified. The compiler can evaluate operands in any order, and may choose another order when the same expression is evaluated again

As the order of evaluation is not always the same hence you may get unexpected results.

Order of evaluation

Praveen
  • 8,945
  • 4
  • 31
  • 49