I would say they were wrong to include such a question.
Except as noted, the following excerpts are all from §[intro.execution] of N4618 (and I don't think any of this stuff has changed in more recent drafts).
Paragraph 16 has the basic definition of sequenced before
, indeterminately sequenced
, etc.
Paragraph 18 says:
Except where noted, evaluations of operands of individual operators and of subexpressions of individual expressions are unsequenced.
In this case, you're (indirectly) calling some functions. The rules there are fairly simple as well:
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. For each function invocation F, for every evaluation A that occurs within F and every evaluation B that does not occur within F but is evaluated on the same thread and as part of the same signal handler (if any), either A is
sequenced before B or B is sequenced before A.
Putting that into bullet points to more directly indicate order:
- first evaluate the function arguments, and whatever designates the function being called.
- Evaluate the body of the function itself.
- Evaluate another (sub-)expression.
No interleaving is allowed unless something starts up a thread to allow something else to execute in parallel.
So, does any of this change before we're invoking the functions via operator overloads rather than directly? Paragraph 19 says "No":
The sequencing constraints on the execution of the called function (as described above) are features of the function calls as evaluated, whatever the syntax of the expression that calls the function might be.
§[expr]/2 also says:
Uses of overloaded operators are transformed into function calls as described
in 13.5. Overloaded operators obey the rules for syntax and evaluation order specified in Clause 5, but the requirements of operand type and value category are replaced by the rules for function call.
Individual operators
The only operator you've used that has somewhat unusual requirements with respect to sequencing are the post-increment and post-decrement. These say (§[expr.post.incr]/1:
The value computation of the ++ expression is sequenced before the modification of the operand object. With respect to an indeterminately-sequenced function call, the operation of postfix ++ is a single evaluation. [ Note: Therefore, a function call shall not intervene between the lvalue-to-rvalue conversion and the side effect associated with any single postfix ++ operator. —end note ]
In the end, however, this is pretty much just what you'd probably expect: if you pass x++
as a parameter to a function, the function receives the previous value of x
, but if x
is also in scope inside the function, x
will have the incremented value by the time the body of the function starts to execute.
The +
operator, however, does not specify ordering of the evaluation of its operands.
Summary
Using overloaded operators does not enforce any sequencing on the evaluation of sub-expressions within an expression, beyond the fact that evaluating an individual operator is a function call, and has the ordering requirements of any other function call.
More specifically, in this case, b--
is the operand to a function call, and --++a-- ++
is the expression that designates the function being called (or at least the object on which the function will be called--the --
designates the function within that object). As noted, ordering between these two is not specified (nor does operator +
specify an order of evaluating its left vs. right operand).