c++c++17language-lawyerorder-of-execution

Why can the reference to the std::array be evaluated first when assigning to it using a brace enclosed list?


Running the following code:

#include <array>
#include <iostream>

using namespace std;

array<int, 2> a;

array<int, 2>& f() {
    cout << "f" << endl;
    return a;
}

int g(int const x) {
    cout << "g " << x << endl;
    return x;
}

int main() {
    f() = { g(1), g(2) };
}

With certain configurations of g++, it prints the following (Compiler Explorer):

f
g 1
g 2

Reading evaluation order on cppreference, I see:

  1. In every simple assignment expression E1 = E2 and every compound assignment expression E1 @= E2, E2 is sequenced before E1. (since C++17)

I would expect f to be printed last.

(Alternatatively, see C++ Standard [expr.assign.1])

What am I misunderstanding?


Solution

  • This is an old gcc bug and the program is well-formed as per over.match and the desired output of the program is just as what you expect/mention(in your question):

    If either operand has a type that is a class or an enumeration, a user-defined operator function can be declared that implements this operator or a user-defined conversion can be necessary to convert the operand to a type that is appropriate for a built-in operator. In this case, overload resolution is used to determine which operator function or built-in operator is to be invoked to implement the operator. Therefore, the operator notation is first transformed to the equivalent function-call notation as summarized in Table 18 (where @ denotes one of the operators covered in the specified subclause). However, the operands are sequenced in the order prescribed for the built-in operator ([expr.compound]).

    Next, expr.assign:

    The assignment operator (=) and the compound assignment operators all group right-to-left. All require a modifiable lvalue as their left operand; their result is an lvalue of the type of the left operand, referring to the left operand. The result in all cases is a bit-field if the left operand is a bit-field. In all cases, the assignment is sequenced after the value computation of the right and left operands, and before the value computation of the assignment expression. The right operand is sequenced before the left operand. With respect to an indeterminately-sequenced function call, the operation of a compound assignment is a single evaluation.

    Here is the old bug report: Evaluation order of assignment operands.