NPE*_*NPE 32 c++ undefined-behavior language-lawyer c++11 c++03
在C++中,以下是否有未定义的行为:
int i = 0;
(i+=10)+=10;
Run Code Online (Sandbox Code Playgroud)
在我对C和C++中+ =的结果的答案的评论中有一些争论?这里的微妙之处在于默认响应似乎是"是",而正确的答案似乎是"它取决于C++标准的版本".
如果它确实取决于标准的版本,请解释UB的位置和不在的位置.
bam*_*s53 34
tl; dr:在C++ 98和C++ 11中都很好地定义了所执行的修改和读取的顺序(i+=10)+=10,但是在C++ 98中,这还不足以使行为定义.
在C++ 98中,对同一对象的多次修改没有插入序列点会导致未定义的行为,即使这些修改的顺序已经明确指定.该表达式不包含任何序列点,因此它由两个修改组成的事实足以使其行为未定义.
C++ 11没有序列点,只要求对象的修改相对于彼此进行排序,并要求读取同一对象以产生定义的行为.
因此,行为在C++ 98中是未定义的,但在C++ 11中定义良好.
C++ 98子句[expr] 5 p4
除非另有说明,否则单个操作符的操作数的评估顺序和个体表达的子表达式的顺序以及副作用发生的顺序是未指定的.
C++ 98子句[expr.ass] 5.17 p1
赋值操作的结果是赋值发生后存储在左操作数中的值; 结果是一个左值
所以我认为顺序是指定的,但是我没有看到单独就足以在表达式的中间创建一个序列点.并继续[expr] 5 p4的引用:
在前一个和下一个序列点之间,标量对象应通过表达式的计算最多修改其存储值一次.
因此,即使指定了顺序,我认为这对于C++ 98中定义的行为来说还不够.
C++ 11消除了序列点,以便更清晰地了解序列之前和序列之后.C++ 98中的语言被替换为
C++ 11 [intro.execution] 1.9 p15
除非另有说明,否则对单个运算符的操作数和单个表达式的子表达式的评估是不确定的.[...]
如果对标量对象的副作用相对于同一标量对象的另一个副作用或使用相同标量对象的值进行的值计算未被排序,则行为未定义.
C++ 11 [expr.ass] 5.17 p1
在所有情况下,分配的右侧和左侧的操作数的值计算后测序,并赋值表达式的值计算之前.
因此,虽然被排序不足以使C++ 98中定义的行为,但C++ 11已经改变了要求,使得被排序(即,排序)就足够了.
(在我看来,"之前'序列'和'之后'序列'提供的额外灵活性导致了更加清晰,一致和明确的语言.)
当操作序列被明确指定时,即使不足以产生技术上明确定义的行为,任何C++ 98实现似乎都不会实际做任何令人惊讶的事情.作为一个例子,Clang在C++ 98模式下生成的这个表达式的内部表示具有明确定义的行为并且做了预期的事情.
Man*_*rse 20
在C++ 11中,表达式定义明确并将导致i == 20.
来自[expr.ass]/1:
在所有情况下,分配的右侧和左侧的操作数的值计算后测序,并赋值表达式的值计算之前.
这意味着i+=1在左侧的值计算(i+=10)+=10之前对赋值进行排序,然后在最终赋值之前对其进行排序i.
在C++ 03中,表达式具有未定义的行为,因为它导致i被修改两次而没有中间序列点.
小智 13
也许.这取决于C++版本.
在C++ 03中,它是一个明显的UB,在赋值之间没有中间序列点.
在C++ 11中,正如Mankarse所解释的那样,它不再是未定义的 - 带括号的复合赋值在外层复合赋值之前排序,所以没关系.