问题
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).
回答1:
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!
回答2:
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.
来源:https://stackoverflow.com/questions/42997644/preincrement-vs-postincrement-in-terms-of-sequence-points