这是一个愚蠢的问题。:)
[编辑:愚蠢与否,这原来是一个 C++ 特殊问题,请参阅 UPDATE_2]
假设我们有:
int a = 0; // line 1
int b = ++a; // line 2
第 2 行发生的情况是(注意,数字只是标记,没有指定确切的顺序):
= [1: write result of (3) to result of (2)]
/\
[2: take "b" l-value] [3: convert result of (4) to an r-value ]
|
[4: take "a" l-value, "increment" and return it]
(4)中的“写”在(3)中的“读”之前“排序”,并且由于之间没有序列点,因此不能保证副作用发生在(3)之前(还有一个“ read" 在 (4) 本身内部,但在 "write"之前排序,因此不会产生 UB)。
那么,上面的错误在哪里?
[更新,针对没有经验的序列点律师:)]
换句话说,问题是:
无论是左值到右值的转换(“读取”)还是增量(“写入”)的副作用首先发生,似乎都存在“竞争”。
在 C 中,根据JTC1/SC22/WG14 N926“序列点分析” *,这将给出一个 UB(例如,参见示例 5 :) 。
int x,y; (x=y) + x; // UB
请注意,如果使用后增量,则不会出现这种情况,因为 (3) 和 (4) 将构成单个[(3): 取“a”左值,将其转换为右值并返回该右值] “写”副作用延迟到下一个序列点之前的某个地方
_
(*) 这看起来像是 C99 标准委员会成员给出的主题的最清晰的系统原理。
[更新_2]
经验教训:永远不要用 C 规则来判断 C++ :))。我确实想知道为什么 N926(它清楚地描述了 C99 的方式)在产生 l 值的前增量的主题上“不够清楚”。
问题出现了,如何为 C++ 建立类似的基本原理,因为没有,因为即使在 C 案例中,仅解释标准也非常困难,而 C++ 语言标准要复杂得多且晦涩难懂。
[更新_3]
在“通用表达式的不确定性”中有一个处理一些相关主题的讨论(至少在较新的一半) 。,此外,委员会人员在这里讨论了此事(请参阅“222. 序列点和左值返回运算符”)。