sequenced-before 关系及其相关规则是对序列点的先验规则的“整理”,其定义方式与其他内存模型关系如 happens-before 和 synchronizes-with 以便精确指定在哪些情况下可以看到哪些操作和效果。
对于简单的单线程代码,规则的结果不变。
让我们从你的例子开始:
1。 i = ++i;
如果i 是诸如int 这样的内置类型,则不涉及函数调用,一切都是内置运算符。因此会发生 4 件事:
(a)++i的值计算,即original-value-of-i+1
(b) ++i 的副作用,它将 original-value-of-i +1 存储回 i
(c) 赋值的值计算,也就是存储的值,这里是++i的值计算的结果
(d) 赋值的副作用,它将新值存储到i
所有这些都是sequenced-before下面的完整表达式。 (即它们都由语句的最后一个分号完成)
由于++i等价于i+=1,存储值的副作用是sequenced-before值计算 ++i,所以 (b) 是 sequenced-before (a)。
赋值的两个操作数的值计算是sequenced-before赋值本身的值计算,依次sequenced-before 存储值的副作用。因此,(a)在(c)之前排序,(c)在之前排序(d)。
因此我们有 (b) -> (a) -> (c) -> (d),因此在新规则下这是可以的,而在 C++98 下则不行。
如果i 是class,则表达式将为i.operator=(i.operator++()) 或i.operator=(operator++(i)),并且operator++ 调用的所有效果都是sequenced-before 致电operator=。
2。 a[++i] = i;
如果a 是一个数组类型,而i 是一个int,那么表达式也有几个部分:
(a)i的值计算
(b)++i的值计算
(c) ++i 的副作用,它将新值存储回i
(d) a[++i] 的值计算,它为 value 计算索引的a 的元素返回一个左值 >++i
(e) 赋值的值计算,也就是存储的值,这里是i的值计算的结果
(f) 赋值的副作用,将新值存入数组元素a[++i]
再一次,所有这些都是sequenced-before下面的完整表达式。 (即它们都由语句的最后一个分号完成)
同样,由于++i 等价于i+=1,存储值的副作用是先排序值计算 的++i,所以 (c) 是 sequenced-before (b)。
数组索引++i的值计算是*sequenced-before`元素选择的值计算,所以(b)是sequenced的-之前 (d)。
赋值的两个操作数的值计算是sequenced-before赋值本身的值计算,依次sequenced-before 存储值的副作用。因此,(a) 和 (d) 在 (e) 之前排序,(e) 在 sequenced-before (f) 之前。
因此我们有两个序列: (a) -> (d) -> (e) -> (f) 和 (c) -> (b) -> (d) -> (e) -> (f) )。
不幸的是,(a) 和 (c) 之间没有排序。因此,存储到i 的副作用对于i 上的值计算是未排序的,并且代码显示未定义的行为。 C++11 标准的 1.9p15 再次给出了这一点。
如上,如果i 是类类型,那么一切都很好,因为运算符变成了函数调用,从而强加了顺序。
规则
规则比较简单:
-
内置运算符的参数的值计算是sequenced-before运算符本身的值计算。 p>
-
内置赋值运算符或预增量运算符的副作用是sequenced-before结果的值计算。 p>
-
任何其他内置运算符的值计算都是该运算符的副作用sequenced-before。
-
内置逗号运算符左侧的值计算 和 副作用是sequenced-在右侧的值计算 和 副作用之前。
-
一个完整表达式的所有值计算和副作用都是sequenced-before下一个完整表达式。
-
函数调用的参数的值计算和副作用在函数中的第一个完整表达式之前排序。 p>
-
值计算和副作用函数内部都是sequenced-before结果的值计算。
-
对于完整表达式中的任意两个函数调用,一个结果的值计算是对另一个的调用sequenced-before,反之亦然.如果没有其他规则指定排序,编译器可以选择。
因此在a()+b() 中,a() 是 sequenced-before b(),或者 b() 是 sequenced-before a(),但是有没有规则来指定哪个。
-
如果有两个副作用修改同一个变量,而另一个sequenced-before,则代码具有未定义的行为。
-
如果存在修改变量的副作用,以及读取该变量的值计算,并且两者都不是sequenced-before另一方面,代码具有未定义的行为。