如何确定什么是“先于"别人的? [英] How to determine what is 'sequenced before' others?

查看:88
本文介绍了如何确定什么是“先于"别人的?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我经历过 关于未定义行为和排序的[Before/After]这个极好的答案 C ++ 11中的关系.我了解二进制关系的概念,但是却缺少管理排序的新规则.

对于这些熟悉的示例,排序规则如何应用?

  1. i = ++i;
  2. a[++i] = i;

更具体地说,什么是新的C ++ 11排序规则?

我正在寻找一些规则,例如(这是完全编造的)

'='语句的lhs总是在rhs之前排序,因此首先被评估.

如果这些在标准本身中可用,有人可以在此处引用相同的内容吗?

解决方案

先后顺序关系及其相关规则是对先于顺序点的已定义规则的整理"以与其他内存模型关系(如 happens-before synchronizes-with )一致的方式,从而可以精确地指定在哪些情况下可以看到哪些操作和效果.

对于简单的单线程代码,规则的结果不变.

让我们从您的示例开始:

1. i = ++i;

如果i是诸如int之类的内置类型,则不涉及任何函数调用,则所有内容都是内置运算符.因此,发生了4件事:

(a)++i值计算,即 i的原始值 +1

(b)++i副作用,该副作用将 i的原始值 +1存储回i

(c)分配的值计算,它只是存储的值,在这种情况下,是> p的值计算的结果>

(d)赋值的副作用,它将新值存储到i

所有这些东西都是在以下完整表达式之前 sequenced-. (也就是说,它们全部由语句的最后一个分号完成)

由于++i等同于i+=1,因此存储值的副作用先于进行值计算 ++i,因此(b)是先于(a).

一个赋值的两个操作数的值计算先于赋值本身的值计算,这又依次先后存储值的副作用.因此(a)在(c)之前先排序,而(c)在(d)之前先排序.

因此,我们有(b)->(a)->(c)->(d),因此在新规则下可以这样做,而在C ++ 98下则不能.

如果iclass,则表达式将是i.operator=(i.operator++())i.operator=(operator++(i)),并且调用的所有效果都是 调用operator=.

2. a[++i] = i;

如果a是数组类型,并且iint,则表达式又包含几个部分:

(a)i

值计算

(b)++i

值计算

(c)++i副作用,它将新值存储回i

(d)a[++i]值计算,它为通过值计算a元素返回 lvalue . ++i

中的>

(e)分配的值计算,它只是存储的值,在这种情况下,是> p的值计算的结果>

(f)赋值的副作用,它将新值存储到数组元素a[++i]

同样,所有这些事情都是在之前下面的完整表达式中进行的. (也就是说,它们全部由语句的最后一个分号完成)

同样,由于++i等效于i+=1,因此存储值的副作用先于进行值计算 ++i,因此(c)是先于(b).

数组索引++i值计算是*顺序的-在元素选择的值计算之前,因此(b)是顺序的-之前(d).

一个赋值的两个操作数的值计算先于赋值本身的值计算,这又依次先后存储值的副作用.因此(a)和(d)在(e)之前先被排序,而(e)在(f)之前先被先后排序().

因此,我们有两个序列:(a)->(d)->(e)->(f)和(c)->(b)->(d)->(e)->(f ).

不幸的是,(a)和(c)之间没有排序.因此,相对于i上的值计算,存储到i副作用未排序的,并且代码显示 >不确定的行为.再次由C ++ 11标准的1.9p15给出.

如上所述,如果i是类类型的,那么一切都很好,因为运算符成为函数调用,从而强加了排序.

规则

规则相对简单:

  1. 内置运算符的参数的值计算先于运算符本身的值计算 .

  2. 内置赋值运算符或预增量运算符的副作用在结果的值计算之前先后 .

  3. 任何其他内置运算符的值计算先于该运算符的副作用.

  4. 内置逗号运算符左侧的值计算 副作用值计算之前 右侧的

  5. >
  6. 一个完整表达式的所有值计算副作用在下一个完整表达式之前排序.

  7. 函数调用的参数的值计算副作用在函数中的第一个完整表达式之前排序 .

  8. 函数中所有 中的值计算副作用先于的结果的值计算.

  9. 对于完整表达式中的任何两个函数调用,其中一个结果的值计算是对另一个函数的调用 sequenced-before ,或者反之亦然.如果没有其他规则指定顺序,则编译器可以选择.

    因此,在a()+b()中,a()先于 b(),或者b()先于 a(),但是存在没有规则指定哪个.

  10. 如果有两个副作用修改了相同的变量,而另一个都不是 sequenced-before ,则该代码具有不确定的行为.

  11. 如果存在修改变量的副作用和读取该变量的 value计算,并且 sequenced-before 另一个,代码具有未定义的行为.

I went through this excellent answer regarding Undefined Behaviour and Sequenced [Before/After] relations in C++11. I understand the binary relation concepts, but am missing what the new rules governing sequencing are.

For these familiar examples, how do the new sequencing rules apply?

  1. i = ++i;
  2. a[++i] = i;

More specifically, what are the new C++11 sequencing rules?

I am looking for some rules like (this one is completely made up)

The lhs of an '=' statement is always sequenced before the rhs, and is thus evaluated first.

In case these are available in the standard itself, can someone quote the same here?

解决方案

The sequenced-before relationship, and the rules concerning it are a "tidying up" of the prior rules on sequence points, defined in a consistent way with the other memory model relationships such as happens-before and synchronizes-with so that it can be precisely specified which operations and effects are visible under which circumstances.

The consequences of the rules are unchanged for simple single-threaded code.

Let's start with your examples:

1. i = ++i;

If i is a built-in type such as int then there are no function calls involved, everything is a built-in operator. There are thus 4 things that happen:

(a) The value computation of ++i, which is original-value-of-i +1

(b) The side effect of ++i, which stores original-value-of-i +1 back into i

(c) The value computation of the assignment, which is just the value stored, in this case the result of the value computation of ++i

(d) The side effect of the assignment, which stores the new value into i

All of these things are sequenced-before the following full expression. (i.e. they are all complete by the final semicolon of the statement)

Since ++i is equivalent to i+=1, the side effect of storing the value is sequenced-before the value computation of ++i, so (b) is sequenced-before (a).

The value computation of both operands of an assignment is sequenced-before the value computation of the assignment itself, and that is in turn sequenced-before the side effect of storing the value. Therefore (a) is sequenced before (c), and (c) is sequenced-before (d).

We therefore have (b) -> (a) -> (c) -> (d), and this is thus OK under the new rules, whereas it was not OK under C++98.

If i was a class, then the expression would be i.operator=(i.operator++()), or i.operator=(operator++(i)), and all effects of the operator++ call are sequenced-before the call to operator=.

2. a[++i] = i;

If a is an array type, and i is an int, then again the expression has several parts:

(a) The value computation of i

(b) The value computation of ++i

(c) The side effect of ++i, which stores the new value back into i

(d) The value computation of a[++i], which returns an lvalue for the element of a indexed by the value computation of ++i

(e) The value computation of the assignment, which is just the value stored, in this case the result of the value computation of i

(f) The side effect of the assignment, which stores the new value into the array element a[++i]

Again, all of these things are sequenced-before the following full expression. (i.e. they are all complete by the final semicolon of the statement)

Again, since ++i is equivalent to i+=1, the side effect of storing the value is sequenced-before the value computation of ++i, so (c) is sequenced-before (b).

The value computation of the array index ++i is *sequenced-before` the value computation of the element selection, so (b) is sequenced-before (d).

The value computation of both operands of an assignment is sequenced-before the value computation of the assignment itself, and that is in turn sequenced-before the side effect of storing the value. Therefore (a) and (d) are sequenced before (e), and (e) is sequenced-before (f).

We therefore have two sequences: (a) -> (d) -> (e) -> (f) and (c) -> (b) -> (d) -> (e) -> (f).

Unfortunately, there is no ordering between (a) and (c). Thus a side effect which stores to i is unsequenced with respect to a value computation on i, and the code exhibits undefined behaviour. This is again given by 1.9p15 of the C++11 standard.

As above, if i is of class type then everything is fine, because the operators become function calls, which impose sequencing.

The rules

The rules are relatively straightforward:

  1. The value computations of the arguments of a built-in operator are sequenced-before the value computation of the operator itself.

  2. The side effects of a built-in assignment operator or preincrement operator are sequenced-before the value computation of the result.

  3. The value computation of any other built-in operator is sequenced-before the side effects of that operator.

  4. The value computation and side-effects of the left-hand side of the built-in comma operator are sequenced-before the value computation and side-effects of the right-hand side.

  5. All value computations and side effects of a full expression are sequenced-before the next full expression.

  6. The value computation and side effects of the arguments of a function call are sequenced before the first full expression in the function.

  7. The value computation and side effects of everything inside a function are sequenced-before the value computation of the result.

  8. For any two function calls in the full expression, either the value computation of the result of one is sequenced-before the call to the other, or vice-versa. If no other rule specifies the ordering, the compiler may choose.

    Thus in a()+b(), either a() is sequenced-before b(), or b() is sequenced-before a(), but there is no rule to specify which.

  9. If there are two side effects that modify the same variable, and neither is sequenced-before the other, the code has undefined behaviour.

  10. If there is a side effect that modifies a variable, and a value computation that reads that variable, and neither is sequenced-before the other, the code has undefined behaviour.

这篇关于如何确定什么是“先于"别人的?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆