24

Take this example code:

int a = 10;
int b = 20;
int c = 30;

int & foo1() {
    qDebug() << "foo1" << endl;
    return a;
}

int & foo2() {
    qDebug() << "foo2" << endl;
    return b;
}

int & foo3() {
    qDebug() << "foo3" << endl;
    return c;
}

int main(void)
{
    foo1() = foo2() = foo3() = 7;
}

Since assignment goes right to left, I expected to see foo3 first and foo1 last, but it is the opposite.

Are the rules for such scenarios concretely defined and how? Also, does the compiler differentiate between assignment and other operators and how would that be possible if you are using the = operator in a different context than initialization? Maybe chain assignment is treated differently from other chaining?

double-beep
  • 5,031
  • 17
  • 33
  • 41

3 Answers3

24

The full expression

foo1() = foo2() = foo3() = 7

can be abstracted with the following tree:

     =
   /   \
foo1()   = 
       /   \
    foo2()   =
           /   \
        foo3()   7

The leaves of that tree can be evaluated in any order. Your compiler is free to choose. Only for calling the assignment operator the expressions hanging on them must be evaluated first. In your case the leaves get evaluated in the order foo1(), foo2() and then foo3().

The right to left associativity of = is only seen in the shape of the tree, but not in the order of evaluation. The tree for

std::cout << foo1() << foo2() << foo3()

looks like

                   << 
                 /    \
              <<      foo3()
            /    \
         <<      foo2()
       /    \
std::cout   foo1()

Again the foo functions may be evaluated in any order, but the order of evaluations of the operator<<() is well-defined. There is an interesting post about sequence points which describes the topics very well.

Community
  • 1
  • 1
Ralph Tandetzky
  • 22,780
  • 11
  • 73
  • 120
  • +1 for the thorough answer and the ascii art, lets round you up to 2.5k rep –  Jun 24 '13 at 15:29
15

Operator associativity (i.e. right-to-left) is unrelated to evaluation order.* The evaluation order of the operands is unspecified.


* Except in a few cases, namely &&, || and ,.
Oliver Charlesworth
  • 267,707
  • 33
  • 569
  • 680
  • And even for `&&` and `||` only the builtin versions have a specified order (which is why you should never ever overload them). – Mark B Jun 24 '13 at 14:54
2

The order how subexpressions are evaluated is not the same as how their result is applied!

foo1() can be called early, just the assignment itself must not be applied before the others are also done.

Balog Pal
  • 16,195
  • 2
  • 23
  • 37