3

In this answer there're some examples of well-defined and undefined expressions. I'm particularly interested in two of them:

(6) i = i++ + 1;    // Undefined Behaviour
(7) i = ++i + 1;    // Well-defined Behaviour

This means that there's a difference between pre-increment and post-increment in terms of sequence points and well defined /unspecified/undefined behavior, but I don't understand where this difference comes from.

In standard draft (N4618) there's an example of code ([intro.execution], pt 18)

i = i++ + 1; // the value of i is incremented

i = i++ + i; // the behavior is undefined

Which, as far as I understand, means that expression i = i++ + 1 should be well-defined and the value of a variable i should increase by 1 as the result of this expression. However, this code run in MSVS 2015 increases i by 2.

So, what happens in the expression i = i++ + 1? Is it well-defined, undefined, implementation-defined or unspecified behavior? And is there any difference between pre-increment and post-increment in this and similar expressions in terms of sequence points and UB, as stated in the original answer? And why Visual Studio shows the behavior which is different from written in standard?

Please also note that I'm primarily interested in modern c++ (14/17).

Community
  • 1
  • 1
alexeykuzmin0
  • 6,344
  • 2
  • 28
  • 51
  • 3
    Sequence points do not exist since C++11. – BoBTFish Mar 24 '17 at 10:56
  • 1
    I am guessing (nothing to back it up) that i++ is expressing "after this line is executed, I expect i to be incremented", but since the line modifies i, the result depends on when the compiler decides to do that increment. – OMGtechy Mar 24 '17 at 10:56
  • 1
    @BoBTFish I meant the relations "Sequenced Before, Sequenced After, Unsequenced and Indeterminately Sequenced" and wrote just "sequence points" for simplicity. Fell free to adjust the text, English is not my native. – alexeykuzmin0 Mar 24 '17 at 10:58
  • 1
    @alexeykuzmin0 No that's fine, as long as you know. – BoBTFish Mar 24 '17 at 11:01
  • @OMGtechy As far as I understand, `i++` is the left operand of `+`, which means it should be sequenced before the `+` operation takes place, and all its side-effects should already take place by the time `+` is calculated. – alexeykuzmin0 Mar 24 '17 at 11:01
  • @alexeykuzmin0 no, because the + is essentially a function call, and the order of evaluations for args to function calls is undefined :) – OMGtechy Mar 24 '17 at 11:02
  • @OMGtechy The order of evaluation of arguments of `+` is indeed unspecified. These arguments are `i++` and `1` and both should be evaluated before `+`. But we don't care about the relative order of `i++` and `1` calculation, do we? – alexeykuzmin0 Mar 24 '17 at 11:04
  • 1
    Decomposing (6) `i = ++i + 1` => `i = operator+(++i, 1)` ==> `i = operator+((i=i+1), 1)` looks good to me – Richard Critten Mar 24 '17 at 11:08
  • @RichardCritten Can't we do the same for `(7)`? `i = i++ + 1` => `i = operator+(i++, 1)`? Is there a difference in terms "well-defined" / "UB" and why? – alexeykuzmin0 Mar 24 '17 at 11:11
  • @alexeykuzmin0 try to write the next line, you stopped 1 expansion too early (ie expand the `i++` part) – Richard Critten Mar 24 '17 at 11:13
  • @RichardCritten I'm not sure I understand you. Could you please write a bit more in detail in form of an answer? – alexeykuzmin0 Mar 24 '17 at 11:14
  • +1 For Richard Critten's comment. #6 looks okay to me. The quote from the standard is correct because we're not sure what order the arguments to `operator+` will be sequenced in. – AndyG Mar 24 '17 at 11:38
  • Also, you should clarify that the standard you cited is the C++17 draft. It doesn't exist in the C++11/14 standards. I used N4618 – AndyG Mar 24 '17 at 11:39
  • @AndyG Agree, will fix the question. – alexeykuzmin0 Mar 24 '17 at 11:40

2 Answers2

2

What happens in the expression i = i++ + 1? Is it well-defined, undefined, implementation defined or unspecified behaviour?

This exact example is given in the standard, how lucky are we?

N4296 1.9.15 [intro.execution]

i = i++ + 1; // the behavior is undefined

Of course, we'd like to know why too. The following standard quote appears to be relevant here:

N4296 1.9.15 [intro.execution]

[ ... ] The value computations of the operands of an operator are sequenced before the value computation of the result of the operator. [ ... ]

This tells us that the sum will occur before the assignment (duh, how else does it know what to assign!), but it doesn't guarantee that the increment will occur before or after the assignment, now we're in murky water...

N4296 1.9.15 [intro.execution]

[ ... ] If a side effect on a scalar object is unsequenced relative to either another side effect on the same scalar object or a value computation using the value of the same scalar object, and they are not potentially concurrent (1.10), the behavior is undefined. [ ... ]

The assignment operator has a side effect on the value of i, which means we have two side effects (the other is the assignment performed by i++) on the same scalar object, which are unsequenced, which is undefined.

Why does Visual Studio show the behavior which is different from written in standard?

It doesn't. The standard says it's undefined, which means it can do anything from what you wanted to something completely different, it just so happens that this is the behaviour that got spat out by the compiler!

OMGtechy
  • 7,935
  • 8
  • 48
  • 83
  • Wow, looks like this exact line was changed between C++14 and C++17. Check in **N4618**: `i = i++ + 1; // the value of i is incremented`. Thank you for detailed explanation about C++14. Also, am I right that `i = ++i + 1` is well-defined in C++14 because side-effect of `++i` and its value computation are sequenced? – alexeykuzmin0 Mar 24 '17 at 11:39
  • 1.9.15 in N4296 directly contradicts 1.9.18 in N4618 by the way. – AndyG Mar 24 '17 at 11:41
  • @alexeykuzmin0 I think so, because in order to do the computation, one must have the correct value of `i` first! – OMGtechy Mar 24 '17 at 11:43
  • @AndyG thanks, if I get the time I might edit the answer to show that too. Feel free to do it yourself if you have the time too; I'm doing this in between compiles ;) – OMGtechy Mar 24 '17 at 11:47
  • The question is rather why this was listed as well-defined: `i = ++i + 1; // Well-defined Behaviour`. Which I believe is because of a change in wording of how the assignment operator behaves. In C11 for example, the text regarding sequence points is identical to C++11, but the texts regarding assignment are different. You don't quote the part about the assignment operator so I don't see how this answers the question. According to your answer, `i = ++i + 1;` would still be UB in C++11. – Lundin Mar 24 '17 at 11:48
  • @Lundin you are right, this doesn't completely answer the question, only partially. Will improve time-permitting :) – OMGtechy Mar 24 '17 at 11:49
  • Looking at this closer, I don't think this answer explains anything at all. Posted a different answer which contracts this, as well as the linked wiki. – Lundin Mar 24 '17 at 12:25
0

i = ++i + 1; // Well-defined Behaviour

This means that there's a difference between preincrement and postincrement in terms of sequence points and well defined / unspecified / undefined behavior, but I don't understand where this difference comes from.

I believe that post is incorrect in several ways. Quoting the same section as they do, emphasis mine:

C++11 1.9/15
The value computations of the operands of an operator are sequenced before the value computation of the result of the operator. If a side effect on a scalar object is unsequenced relative to either another side effect on the same scalar object or a value computation using the value of the same scalar object, the behavior is undefined.

Then the assignment operator:

C++11 5.17
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.

Notable, the value computation or the right and left operands is not sequenced. (This has been explicitly spelled out in C11 1), which otherwise has a completely identical text as C++11.)

Meaning that in the expression i = ++i + 1; the side effect of ++i is unsequenced in relation to the value computation of the left operand i. Thus it is undefined behavior as per 1.9/15. And the UB has nothing to do with the assignment side-effect at all.

As for the expression i = i++ + 1;, the side-effect of assignment is, as per C++11, explicitly sequenced after the value computations, but before the value computation of the expression as whole. The value computation of i++ is not an issue as per 5.2.6 "The value computation of the ++ expression is sequenced before the modification of the operand object". As per the nature of the postfix ++, the side effect of updating i++ must be sequenced after the value computation of the whole expression. It is well-defined behavior, as far as I can tell.

Correct text should therefore be

(6) i = i++ + 1;    // Well-defined Behaviour
(7) i = ++i + 1;    // Undefined Behaviour

Apparently there was an incorrect example in C++11 1.9/15 i = i++ + 1; // the behavior is undefined which has been corrected in later versions of the standard.

NOTE: None of this has the slightest to do with the change of wording about sequence points!


1) C11 6.5.16/3

The side effect of updating the stored value of the left operand is sequenced after the value computations of the left and right operands. The evaluations of the operands are unsequenced.

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • In the expression `(*p)+=++(*q) +1;` the value computations of both `p` and `(*p)` would be unsequenced relative to the value computation of `(*q)`, but when using a straight assignment operator with primitive types (e.g. `(*p)= ++(*q) +1;`) the only value computation on the left-hand side would be `p`; any pre-existing value of `*p` will be obliterated without being used. If `q` held the address of `p`, then `(*p)= ++(*q) + 1` would invoke UB because the fetch of pointer `p`p and the modification of `*q` would be unsequenced. I don't understand, however, your basis for saying that... – supercat Mar 24 '17 at 22:23
  • ...the modification performed by postfix `++` will be sequenced after the remainder of the *enclosing* expression. Given e.g. `(*p)= (*q)++ + 57;`, if `p` and `q` happen to equal identify the same initially-zero location, would you expect that to store 1, 57, or 58? I would think all three would be plausible, and see nothing that would mandate a particular choice. – supercat Mar 24 '17 at 22:29