javapythonc++operatorscompound-assignment

Why does compound assignment (+=) differ between languages (Java, C++)?


The definitions of += seem to be the same in both Java and C++, however, they perform differently.

Consider the following code in C++:

#include <iostream>

int n;
int f(int x) {
    n += x;
    return x;
}
int main() {
    n = 0;
    n = n + f(3);
    std::cout<<n<<" ";
    n = 0;
    n += f(3);
    std::cout<<n<<" ";
    n = 0;
    n = f(3) + n;
    std::cout<<n<<std::endl;
}

This outputs: 3 6 6

Similar code in Java outputs: 3 3 6, here is the code for reference.

static int n;
public static void main(String[] args) {
    n = 0;
    n = n + f(3);
    System.out.println(n);
    n = 0;
    n += f(3);
    System.out.println(n);
    n = 0;
    n = f(3) + n;
    System.out.println(n);
}
public static int f(int x) {
    n += x;
    return x;
}

Looking at the documentation for C++ and Java, they write similar definitions:

C++:

E1 op= E2 (where E1 is a modifiable lvalue expression and E2 is an rvalue expression or a braced-init-list (since C++11)) is exactly the same as the behavior of the expression E1 = E1 op E2, except that the expression E1 is evaluated only once and that it behaves as a single operation with respect to indeterminately-sequenced function calls

Java:

A compound assignment expression of the form E1 op= E2 is equivalent to E1 = (T) ((E1) op (E2)), where T is the type of E1, except that E1 is evaluated only once.

Out of curiosity, I checked this in Python, and it has the same output as Java. Of course, writing code like this is extremely bad practice, but I'm still curious for an explanation.

I suspect that the ordering of the way variables get evaluated is different for += in different languages, but I don't know exactly how. What in the definitions am I missing, and how are compound assignment operators evaluated?


Solution

  • This has more to do with evaluation order than "what the compound assignment operators do", so you'll find more useful things in the "evaluation order" sections of the specs of both languages.

    For Java, JLS §15.7:

    The left-hand operand of a binary operator appears to be fully evaluated before any part of the right-hand operand is evaluated.

    If the operator is a compound-assignment operator (§15.26.2), then evaluation of the left-hand operand includes both remembering the variable that the left-hand operand denotes and fetching and saving that variable's value for use in the implied binary operation.

    So n on the left of += evaluates first, to 0. Then the right hand side evaluates to 3. The sum of this value and the left hand side is then written to n.

    For C++, Evaluation Order:

    Look at item 20 in the "Rules" section:

    In every simple assignment expression E1=E2 and every compound assignment expression E1@=E2, every value computation and side-effect of E2 is sequenced before every value computation and side effect of E1

    Here, E2 (the right hand side) is evaluated first, to 3, then the left hand side is evaluated. At that point, n is already changed to 3 by f, so the left hand side evaluates to 3 as well.