Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ cout side-effect sequencing

Suppose following piece of code:

#include <iostream>
using namespace std;

char one()
{
    cout << "one\n";
    return '1';
}

char two()
{
    cout << "two\n";
    return '2';
}

int main(int,char**)
{
    // 1:
    cout << one()
         << '\n'
         << two()
         << '\n';

    // 2:
    operator<<(
        operator<<(
            operator<<(
                operator<<(
                    cout, 
                    one()),
                '\n'),
            two()),
        '\n');
}

execution of lines marked as 1 and 2, being compiled with ideone does the same, it prints following:

two
one
1
2

From my point of view, what we observe here is unspecified behaviour, as order in which function arguments are resolved is unspecified.

This was a question at an interview, printing above given sequence (without any alternatives) was supposed to be correct answer, but is it really correct?

like image 216
sukhmel Avatar asked Aug 23 '14 20:08

sukhmel


2 Answers

You are correct, and the interviewer shows a frighteningly common lack of understanding about the language and its rules.

Those two lines are strictly equivalent, iff every operator<< called for the first line is always a free function (The standard says they are).

As you rightly thought, the ordering between the function-calls, except where ones arguments are the return-value of another, are indeterminately sequenced (before or after, but unspecified which):

1.9 Program execution [intro.execution]

[...]
15 [...]
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.9 Several contexts in C++ cause evaluation of a function call, even though no corresponding function call syntax appears in the translation unit. [ Example: Evaluation of a new expression invokes one or more allocation and constructor functions; see 5.3.4. For another example, invocation of a conversion function (12.3.2) can arise in contexts in which no function call syntax appears. —end example ] 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.

Naming all the parts:

cout << one() // a) execute one()           ("one\n")
              // b) output the return-value ("1")
     << '\n'  // c) output newline          ("\n")
     << two() // d) execute two()           ("two\n")
              // e) output the return-value ("2")
     << '\n'; // f) output newline          ("\n")

Ordering constraints:

a < b < c < e < f
d < e < f

Or a different representation:

a < b < c <
          < e < f
d         <

Thus, all valid full orders:

abcdef "one\n1\ntwo\n2\n"
abdcef "one\n1two\n\n2\n"
adbcef "one\ntwo\n1\n2\n"
dabcef "two\none\n1\n2\n"
like image 172
Deduplicator Avatar answered Oct 27 '22 04:10

Deduplicator


You are correct, but the interview answer is wrong.

Per Paragraph §1.9/15 of the C++11 Standard:

Except where noted, evaluations of operands of individual operators and of subexpressions of individual expressions are unsequenced.

As an example, this is what Clang 3.4 produces:

one
1
two
2
like image 33
David G Avatar answered Oct 27 '22 04:10

David G