0

I'm surprised that this works:

double x = 3;
double y = 2;

(x *= 2) += y;
std::cout << x << std::endl;

The result is 8, which is what it looks like the programmer is trying to achieve. But I thought assignment operators returned an rvalue - how is it that you can assign to the result of one?

Tom
  • 7,269
  • 1
  • 42
  • 69
  • 4
    Related: [What's the result of += in C and C++?](http://stackoverflow.com/questions/10653903/whats-the-result-of-in-c-and-c/10654019#10654019). – Sergey Kalinichenko May 13 '13 at 10:51

3 Answers3

2

The assignment operators for the built in types return an lvalue in C++ (unlike in C). But you cannot use it to modify the object without an intervening sequence point, so your example is undefined behavior (in C++03—C++11 changed a lot here, and I seem to remember that one of the results made your code defined).

Regardless of the situation with regards to undefined behavior, you would be better off writing:

x = 2 * x + y;

It's far more readable. The fact that the assignment operators result in lvalues is really only usable when the results are bound immediately to a reference:

T&
SomeClass::f()
{
    //  ...
    return aTinSomeClass += 42;
}

And even then, I'd write it in two statements.

(The general rule in C++ is that if the result of an operator corresponds to the value of an object in memory, then it is an lvalue. There was no general rule in C.)

James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • I certainly agree that your version is more readable - I'm going over someone else's (old) code and this is only one of the... curiosities it has thrown up. Can you expand on the sequence point a bit? Perhaps with a reference to the standard for "you cannot use it to modify the object without an intervening sequence point"? – Tom May 13 '13 at 11:04
  • The reference in pre C++11 is paragraph 4 of section 5: "Between the previous and next sequence point a scalar object shall have its stored value modified at most once by the evaluation of an expression." Sequence points are the end of a full expression, calling or returning from a function, and a very few operators: `&&`, `||`, `?:` and the comma operator (but not a comma used e.g. to separate function arguments). – James Kanze May 13 '13 at 11:20
  • Thanks. Curious that `g++ -Wsequence-point` doesn't complain about this (g++ 4.6.3). – Tom May 13 '13 at 11:25
  • @Tom: In C++11, the behaviour is defined: 5.17/1 says "the assignment is sequenced after the value computation of the right and left operands, and before the value computation of the assignment expression". But I agree that you should stick with the readable version. – Mike Seymour May 13 '13 at 11:39
  • @MikeSeymour Yes. I had some sort of vague remembrance that C++11 made it defined. My comments with regards to undefined behavior are based on C++03; I've not really had time to study all of the implications on the new specification (which extend to cover multithreaded access). – James Kanze May 13 '13 at 13:21
0

In C++, compound assignment operators return lvalues, as per §5.17/1:

The assignment operator (=) and the compound assignment operators all group right-to-left. All require a modifiable lvalue as their left operand and return an lvalue referring to the left operand.

In this case, the use of *= returns an lvalue denoting the object x which is then used as the left operand of the += operator.

Perhaps you are thinking of the simple arithmetic operators like + and * which do return rvalues.

In C, these operators don't return lvalues and therefore the result can't be further assigned.

Joseph Mansfield
  • 108,238
  • 20
  • 242
  • 324
0

In C++ the result of the assignment operators, including the compound assignment operators (such as *=) are l-values, and thus assignables.

In C they are r-values, so your code invalid C code.

rodrigo
  • 94,151
  • 12
  • 143
  • 190