int i = 0;
(i+=10)+=10;
在我的回答中对C和C ++中+=的结果是什么?有一些争议。这里的微妙之处在于默认响应似乎是“是”,而正确答案似乎是“取决于C ++标准的版本”。
如果它确实取决于标准的版本,请解释在哪里是未定义行为,在哪里不是。
int i = 0;
(i+=10)+=10;
tl;dr: 在C++98和C++11中,(i+=10)+=10
的修改和读取顺序都被明确定义。然而在C++98中,即使修改顺序被明确定义,如果同一对象没有一个中间的sequence point,多次对其进行修改也会导致未定义行为。而C++11已经废除了sequence point的概念,只要保证对象的修改顺序相对于读取顺序是有序的,就可以得到定义良好的行为。
C++98规定,在没有中间sequence point的情况下,对同一对象进行多次修改会导致未定义行为,即使修改顺序被明确定义。而C++11已经废除了sequence point的概念,只需要保证对象的修改顺序相对于读取顺序是有序的,就可以得到定义良好的行为。
C++11 [intro.execution] 1.9 p15
除非另有说明,否则单个操作符的操作数以及单个表达式的子表达式的计算是无序的。[...]
如果对标量对象的副作用与同一标量对象的另一个副作用或使用相同标量对象值进行的值计算之间的顺序关系不确定,则行为未定义。
C++11 [expr.ass] 5.17 p1
在所有情况下,赋值在右和左操作数的值计算之后顺序执行,并在赋值表达式的值计算之前顺序执行。
因此,尽管有序并不足以使行为在C ++98中被定义,但C ++11已更改要求,即有序(即,按顺序)就足够了。
(而且我认为“序列之前”和“序列之后”提供的额外灵活性已经导致了一种更加明确,一致和规范的语言。)
我认为,即使顺序操作的序列不足以产生技术上定义良好的行为,任何C++98实现也不太可能在其序列化时做出令人惊讶的事情。例如,Clang在C ++98模式下生成的此表达式的内部表示具有定义良好的行为并执行所期望的操作。
i
的副作用与赋值表达式i+=10
的值计算是分开且顺序的。赋值在左侧或右侧的值计算之后进行。参见5.17/1。 - bames53[expr.ass]/1
可以得知:
这意味着赋值i+=1在(i+=10)+=10的左手边值计算之前进行排序,该左手边值计算又排在对i的最终赋值之前。在所有情况下,赋值都在右手和左手操作数的值计算之后进行排序,在赋值表达式的值计算之前进行。
或许吧。这取决于C++的版本。
在C++03中,这是明显的未定义行为,因为赋值之间没有干预序列点。
在C++11中,正如Mankarse所解释的那样,这不再是未定义的行为了——括号内的复合赋值在外部赋值之前发生,所以没问题。
T&
的 f
,这样您就可以同时执行 f(x = 5)
和 f(x += 5)
。 - user784668f(x = 5)
也不会编译通过,也就是f(x += 5)
同样如此(当f
需要一个int&
时)。这有什么不一致之处呢? - Mankarseint& f(int& i) { return i += 2; }
这样的东西。 - James Kanze