简体   繁体   中英

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?

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.

Evaluation order - strictly left-right in Java.

n += f(3);

So: 'n' is 0. f(3) returns 3. So we add 0 and 3, and assign the result to n. The assignment to n inside f() has no effect.

Java language spec:

[…] the value of the left-hand operand is saved and then the right-hand operand is evaluated. […]

For C++, I believe (but did not check) that evaluation order is undefined.

In your case, f(3) was called, n became 3, and then the result of f(3) was added to the new value of n.


To determine the meaning of an expression, you can't just look at the operators involved. Evaluation order matters, and where a variable is modified and used in the same expression, the fine print matters (the result may or may not even be defined in the language).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM