Recently in an interview there was a following objective type question.
int a = 0;
cout << a++ << a;
Answers:
a. 10
The correct answer is to question the question. The statement is unacceptable because a reader cannot see a clear answer. Another way to look at it is that we have introduced side-effects (c++) that make the statement much harder to interpret. Concise code is great, providing it's meaning is clear.
Technically, overall this is Undefined Behavior.
But, there are two important aspects to the answer.
The code statement:
std::cout << a++ << a;
is evaluated as:
std::operator<<(std::operator<<(std::cout, a++), a);
The standard does not define the order of evaluation of arguments to an function.
So Either:
std::operator<<(std::cout, a++)
is evaluated first or a
is evaluated first or This order is Unspecified[Ref 1] as per the standard.
[Ref 1]C++03 5.2.2 Function call
Para 8
The order of evaluation of arguments is unspecified. All side effects of argument expression evaluations take effect before the function is entered. The order of evaluation of the postfix expression and the argument expression list is unspecified.
Further, there is no sequence point between evaluation of arguments to a function but a sequence point exists only after evaluation of all arguments[Ref 2].
[Ref 2]C++03 1.9 Program execution [intro.execution]:
Para 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.
Note that, here the value of c
is being accessed more than once without an intervening sequence point, regarding this the standard says:
[Ref 3]C++03 5 Expressions [expr]:
Para 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.
The code modifies c
more than once without intervening sequence point and it is not being accessed to determine the value of the stored object. This is clear violation of the above clause and hence the result as mandated by the standard is Undefined Behavior[Ref 3].
Sequence points only define a partial ordering. In your case, you have (once overload resolution is done):
std::cout.operator<<( a++ ).operator<<( a );
There is a sequence point between the a++
and the first call to
std::ostream::operator<<
, and there is a sequence point between the
second a
and the second call to std::ostream::operator<<
, but there
is no sequence point between a++
and a
; the only ordering
constraints are that a++
be fully evaluated (including side effects)
before the first call to operator<<
, and that the second a
be fully
evaluated before the second call to operator<<
. (There are also
causual ordering constraints: the second call to operator<<
cannot
preced the first, since it requires the results of the first as an
argument.) §5/4 (C++03) states:
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. 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.
One of the allowable orderings of your expression is a++
, a
, first
call to operator<<
, second call to operator<<
; this modifies the
stored value of a
(a++
), and accesses it other than to determine
the new value (the second a
), the behavior is undefined.
You can think of:
cout << a++ << a;
As:
std::operator<<(std::operator<<(std::cout, a++), a);
C++ guarantees that all side effects of previous evaluations will have been performed at sequence points. There are no sequence points in between function arguments evaluation which means that argument a
can be evaluated before argument std::operator<<(std::cout, a++)
or after. So the result of the above is undefined.
C++17 update
In C++17 the rules have been updated. In particular:
In a shift operator expression
E1<<E2
andE1>>E2
, every value computation and side-effect ofE1
is sequenced before every value computation and side effect ofE2
.
Which means that it requires the code to produce result b
, which outputs 01
.
See P0145R3 Refining Expression Evaluation Order for Idiomatic C++ for more details.