Pockets
Pockets

Reputation: 1264

C++ Order of Evaluation

I'm trying to figure out if there's anything in the C++11 specification re. expected behavior for the following code (GitHub link here):

struct Scalar {
    int data;

    Scalar(int x) : data(x) {}

    int get() {
        return data;
    }

    Scalar &square() {
        scale(data);
        return *this;
    }

    void scale(int rhs) {
        data *= rhs;
    }
};

int main() {
    Scalar v(3);

    v.square().scale(v.get());

    return v.data;
}

This comes up mostly because of the discovery that this does different things between g++ and clang++:

$ g++ --version
g++ (GCC) 6.2.1 20160830
Copyright (C) 2016 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ clang++ --version
clang version 3.9.0 (tags/RELEASE_390/final)
Target: x86_64-unknown-linux-gnu
Thread model: posix
InstalledDir: /usr/bin

$ g++ -std=c++11 order_of_ops.cpp -o a.out && ./a.out; echo $?
27

$ clang++ -std=c++11 order_of_ops.cpp -o a.out && ./a.out; echo $?
81

The answer seems like it should be in § 5.2.2 and § 5.2.5 of n3242 but I'm having trouble tracking down something concrete.

Upvotes: 3

Views: 135

Answers (1)

krzaq
krzaq

Reputation: 16421

If I read things correctly, the behaviour of your code is unspecified. N3337 for C++11 quotes:

§ 1.9 [intro.execution] / 15

Except where noted, evaluations of operands of individual operators and of subexpressions of individual expressions are unsequenced. [...] 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, the behavior is undefined.

but it follows with

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

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

and

§ 5.2.2 [expr.call] / 8

[ Note: The evaluations of the postfix expression and of the argument expressions are all unsequenced relative to one another. All side effects of argument expression evaluations are sequenced before the function is entered (see 1.9). —end note ]

So, your modification and unrelated read of Scalar::data are indeterminately sequenced.

That being said, it is likely to change and be well-defined in C++1z:

N4606 § 5.2.2 [expr.call] / 5

The postfix-expression is sequenced before each expression in the expression-list and any default argument. The initialization of a parameter, including every associated value computation and side effect, is indeterminately sequenced with respect to that of any other parameter.

Thus, in C++1z your v.data should be equal to 81 (if I read things correctly)

Upvotes: 8

Related Questions