16

I have started studying about C++0x. I came across the follow expression somewhere:

int l = 1, m=2;
++l *= m;

I have no idea whether the second expression has well defined behavior or not. So I am asking it here.

Isn't it UB? I am just eager to know.

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
Lawrence
  • 163
  • 3

2 Answers2

14

The expression is well defined in C++0x. A very Standardese quoting FAQ is given by Prasoon here.

I'm not convinced that such a high ratio of (literal Standards quotes : explanatory text) is preferable, so I'm giving an additional small explanation: Remember that ++L is equivalent to L += 1, and that the value computation of that expression is sequenced after the increment of L. And in a *= b, value computation of expression a is sequenced before assignment of the multiplication result into a.

What side effects do you have?

  • Increment
  • Assignment of the multiplication result

Both side-effects are transitively sequenced by the above two sequenced after and sequenced before.

Community
  • 1
  • 1
Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • No. IIUC the standard the side effect of (L += 1) is *initiated* after the value computation but you cannot assume that it will *complete* before the other side effect (of *=) is initiated. The code generated be the compiler could wait until the very end of the full-expression before actually complete the update of L. – 6502 Dec 02 '10 at 20:48
  • 1
    @6502 I have read all this out of the draft Standard weeks ago and this has been debated at length on SO elsewhere. My explanation and @Prasoon's Standard quotes should get you started to take n3126 and read in it and verifying the validness of it. The side effect of `L += 1` is initiated by evaluating that expression, and is sequenced *after* value computation of `L` and *before* value computatin of that entire expression. Sequenced-before/after is *transitive*, so if A is sequenced-before B and B is sequenced-before C, then A is sequenced before C. – Johannes Schaub - litb Dec 02 '10 at 21:01
  • I'm not going to discuss this another time here, by all means. – Johannes Schaub - litb Dec 02 '10 at 21:01
  • @Johannes: in 1.9.15 (fourth statement in the example) it's said that `i = i++ + 1` is undefined behaviour. By your explanation `i = ++i + 1` would be instead ok (`++i` increment is sequenced before `+` value computation, that is sequenced before `=`). This is the situation in your opinion with C++0x ? `i=i++ + 1` is UB and `i=++i + 1` is ok ? – 6502 Dec 02 '10 at 21:32
  • @6502 yes that's the case. See http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#637 – Johannes Schaub - litb Dec 02 '10 at 22:14
  • 1
    @Johannes: Ok. I voted for deleting my wrong reply. It has been long I didn't dig into internals of C++ and in retrospect I think it was a good idea. IMO the last thing on earth that C++ needed was increasing complexity and asymmetries... but apparently this is exactly what happened. The pointed discussion says that it was good to remove some UB... but I don't agree; this is obtained by special casing and increasing complexity for UB classification so MORE programmers will make mistakes in those areas. `i = ++i + 1` will work, but by mere coincidence and not by deliberate programmmer's choice. – 6502 Dec 02 '10 at 22:42
  • @Johannes Maybe I'm wrong, but I'm not buying your answer. You state that value computation of `l += 1` is sequenced _after_ increment, which is true. BUT, `l` is also value computed before increment, so to my understanding the statement as a whole does not have a well defined sequence. – Håvard S Dec 02 '10 at 22:53
  • @Håvard yes, `l` is also value computed before the increment. That value computation is sequenced before the increment. So that's fine. – Johannes Schaub - litb Dec 02 '10 at 23:24
  • @Johannes I'm still not following. Intuitively, I can of course understand how it _ought to_ work. But formally, since you have two "sequence before" statements regarding `l` as an lvalue, what determines the final order? – Håvard S Dec 02 '10 at 23:28
  • @Håvard point out where you see a problem, with a paragraph number into n3126, like @6502 did. Then comment and then we can sort it out. You can't get into this discussion and disagree with my answer without having consulted the respective sections in detail first. – Johannes Schaub - litb Dec 02 '10 at 23:30
  • 1
    See my discussion with @6502 in my initial answer. I was unable to grasp that the lvalue computations of `l` were actually sequenced as per 5.17(1). Correlating with 5.3.2.1 and 5.17(7) enlightened me. Thanks for having patience. :) – Håvard S Dec 03 '10 at 00:09
  • What I do not quite understand now: this is not UB with c++0x. Was it UB with current c++? – Suma Dec 03 '10 at 11:50
  • @Suma: Yes, with C++ that is UB because in the same expression there are multiple changes to the variable `L` without intervening sequence points. In C++0x the concept of sequence point was abandoned and there is a stricter and more complex specification on how the evaluation should be carried on... this make that expression valid and with a well defined behaviour. In my opinion not many programmers will understand the subtle points of this and the now increased complexity of the rules will make C++0x even trickier than it is C++ now. – 6502 Dec 03 '10 at 13:26
  • @Suma also see http://stackoverflow.com/questions/4176328/undefined-behavior-and-sequence-points/4190054#4190054 for current C++'s semantics. In my opinion, the new rules of C++0x has made things clearer and easier to understand. – Johannes Schaub - litb Dec 03 '10 at 15:23
  • I agree. There will be less cases of UB in C++0x, because the rules are more consistent. – Suma Dec 03 '10 at 16:15
3

In the code above, prefix ++ has precedence over *=, and so gets executed first. The result is that l equals 4.

UPDATE: It is indeed undefined behavior. My assumption that precedence ruled was false.

The reason is that l is both an lvalue and rvalue in *=, and also in ++. These two operations are not sequenced. Hence l is written (and read) twice "without a sequence point" (old standard wording), and behavior is undefined.

As a sidenote, I presume your question stems from changes regarding sequence points in C++0x. C++0x has changed wording regarding "sequence points" to "sequenced before", to make the standard clearer. To my knowledge, this does not change the behavior of C++.

UPDATE 2: It turns out there actually is a well defined sequencing as per sections 5.17(1), 5.17(7) and 5.3.2(1) of the N3126 draft for C++0x. @Johannes Schaub's answer is correct, and documents the sequencing of the statement. Credit should of course go to his answer.

Håvard S
  • 23,244
  • 8
  • 61
  • 72
  • This is incorrect. See http://stackoverflow.com/questions/4336860/is-l-m-undefined-behaviour/4338764#4338764 – Johannes Schaub - litb Dec 02 '10 at 19:04
  • No, to my understanding this is correct. See my comment on your answer. – Håvard S Dec 02 '10 at 22:58
  • It's not undefined behaviour... but explaining why wouldn't fit in this space. Apparently the validity of such a statement wasn't intentional and is just a side-effect of fixing another issue with the language... none the less the gurus decided it was good for C++ so it's going to remain valid. IMO the net effect is that people will read here that this is valid and will write that kind of code and apparently innocent variations of it (that are however UB) in C++ programs. How tricky can be ? For example `i = i++ + 1` is UB, while `i = ++i + 1` is valid. I'm NOT kidding. – 6502 Dec 02 '10 at 22:59
  • @6502 Could you please enlighten us with a citation? – Håvard S Dec 02 '10 at 23:02
  • @Håvard: it's in Johannes reply to my comments... http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#637 describes how it was found that the fix to issue 222 did unintentionally make `i = ++i + 1` valid code. That code was before an example of UB in 1.9.15 and the decision was to change the example to `i = i++ + 1` that was instead UB even after the fix of 222. – 6502 Dec 02 '10 at 23:10
  • @6502 As my comment to that answer says, these aren't equivalent to the question at hand since as far as I can understand that reasoning ignores a value computation of `l` which yields undefined behavior. – Håvard S Dec 02 '10 at 23:17
  • 1
    The question involves `++L` that is equivalent to `(L+=1)` (see 5.3.2(1)). `L+=1` is a compound assignment (see 5.17(1)). The relevant part is "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.". Now the value of the assignment is needed by `*=` (that is another compound assignment) and is sequenced before the assignment. No multiple unsequenced side effects to L are present so the code is valid. Yes I agree this sucks. – 6502 Dec 02 '10 at 23:34
  • I was just reading 5.3.2(1) and correlating with 5.17(1) (and 5.17(7) regarding equivalence of compound assignments) clears things up. I could not grasp that the lvalue computations of `l` were sequences, but your quote from 5.17(1) clears it up. Thanks! – Håvard S Dec 03 '10 at 00:07
  • @HåvardS ~ So the award should goto @JohannesSchaub then? – jcolebrand Dec 03 '10 at 00:34