In which versions of the C++ standard does “(i+=10)+=10” have undefined behaviour?

前提是你 提交于 2019-12-17 10:53:20

问题


In C++, does the following have undefined behaviour:

int i = 0;
(i+=10)+=10;

There was some debate about this in the comments to my answer to What's the result of += in C and C++? The subtlety here is that the default response seems to be "yes", whereas it appears that the correct answer is "it depends on the version of the C++ standard".

If it does depend on the version of the standard, please explain where it's UB and where it's not.


回答1:


tl;dr: The sequence of the modifications and reads performed in (i+=10)+=10 is well defined in both C++98 and C++11, however in C++98 this is not sufficient to make the behavior defined.

In C++98 multiple modifications to the same object without an intervening sequence-point results in undefined behavior, even when the order of those modifications is well specified. This expression does not contain any sequence points and so the fact that it consists of two modifications is sufficient to render its behavior undefined.

C++11 doesn't have sequence points and only requires that the modifications of an object be ordered with respect to each other and to reads of the same object to produce defined behavior.

Therefore the behavior is undefined in C++98 but well defined in C++11.


C++98

C++98 clause [expr] 5 p4

Except where noted, the order of evaluation of operands of individual operators and subexpressions of individual expression, and the order in which side effects take place, is unspecified.

C++98 clause [expr.ass] 5.17 p1

The result of the assignment operation is the value stored in the left operand after the assignment has taken place; the result is an lvalue

So I believe the order is specified, however I don't see that that alone is enough to create a sequence point in the middle of an expression. And continuing on with the quote of [expr] 5 p4:

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.

So even though the order is specified it appears to me that this is not sufficient for defined behavior in C++98.


C++11

C++11 does away sequence points for the much clearer idea of sequence-before and sequenced-after. The language from C++98 is replaced with

C++11 [intro.execution] 1.9 p15

Except where noted, evaluations of operands of individual operators and of subexpressions of individual expressions are unsequenced. [...]

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.

C++11 [expr.ass] 5.17 p1

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.

So while being ordered was not sufficient to make the behavior defined in C++98, C++11 has changed the requirement such that being ordered (i.e., sequenced) is sufficient.

(And it seems to me that the extra flexibility afforded by 'sequence before' and 'sequenced after' has lead to a much more clear, consistent, and well specified language.)


It seems unlikely to me that any C++98 implementation would actually do anything surprising when the sequence of operations is well specified even if that is insufficient to produce technically well defined behavior. As an example, the internal representation of this expression produced by Clang in C++98 mode has well defined behavior and does the expected thing.




回答2:


In C++11 the expression is well defined and will result in i == 20.

From [expr.ass]/1:

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.

This means that the assignment i+=1 is sequenced before the value computation of the left hand side of (i+=10)+=10, which is in turn sequenced before the final assignment to i.


In C++03 the expression has undefined behavior, because it causes i to be modified twice with no intervening sequence point.




回答3:


Maybe. It depends on C++ version.

In C++03, it's an obvious UB, there's no intervening sequence point between the assignments.

In C++11, as Mankarse explains, it's not undefined anymore — the parenthesized compound assignment is sequenced before the outer one, so it's okay.



来源:https://stackoverflow.com/questions/10655290/in-which-versions-of-the-c-standard-does-i-10-10-have-undefined-behaviou

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!