如何确定别人'sequenced before'什么?

How to determine what is 'sequenced before' others?

本文关键字:什么 before sequenced 何确定 别人      更新时间:2023-10-16

我浏览了这个优秀的答案关于c++ 11中的未定义行为和顺序[Before/After]关系。我理解二元关系的概念,但我不知道控制排序的新规则是什么。

对于这些熟悉的例子,如何排序规则适用吗?
  1. i = ++i;
  2. a[++i] = i;

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

我正在寻找一些规则,如(这一个是完全组成的)

'='语句的lhs总是排在rhs之前,因此先求值。

如果这些在标准本身中可用,有人能在这里引用相同的吗?

之前排序的关系,以及与之相关的规则是"整理";在序列点的先前规则中,以与其他内存模型关系(如同步之前发生)一致的方式定义,以便可以精确指定哪些操作和效果在哪些情况下是可见的。

规则的结果对于简单的单线程代码是不变的。

让我们从你的例子开始:

1。i = ++i;

如果iint一样是内置类型,则不涉及函数调用,一切都是内置操作符。因此,有4种情况发生:

(a)++i值计算,即original-value-of-i+1

(b)++i副作用,将i+1的original-value-of-i存储回i

(c)赋值的值计算,即存储的值,在本例中是++i

值计算

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

所有这些东西都是排序的,在下面的完整表达式之前。(即它们都由语句的最后一个分号完成)

由于++i等价于i+=1,因此存储该值的副作用是在++i值计算之前排序,因此(b)在(a)之前

排序。赋值操作数的值计算

赋值本身的值计算之前排序,然后在存储值的副作用之前排序。因此(a)排在(c)之前,(c)排在(d)之前。

因此有(b) ->(一)→(c)→(d),因此这在新规则下是可以的,而在c++ 98下则不可以。

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

2。a[++i] = i;

如果a是数组类型,iint,那么表达式又有几个部分:

(a)i值计算(b)++i值计算

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

(d)a[++i]值计算,对于++i值计算索引的a元素,返回左值

(e)赋值的值计算,即存储的值,在本例中是i

值计算

的结果(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. 内置操作符的参数值计算

操作符本身的值计算之前排序。
  • 内置赋值操作符或预增量操作符的副作用

  • 结果的值计算之前排序。
  • 任何其他内置操作符的值计算

  • 操作符的副作用之前排序。
  • 值计算内置逗号操作符左侧的副作用值计算之前进行排序。右边的副作用

  • 完整表达式的所有值计算副作用

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

  • 之前排序。
  • 函数内所有值计算副作用结果的值计算之前排序。
  • 对于完整表达式中的任意两个函数调用,一个函数的结果值计算在调用另一个函数之前进行排序,反之亦然。如果没有其他规则指定顺序,编译器可以选择。

    因此在a()+b()中,a()

  • b()之前排序,或者b()a()之前排序,但是没有规则指定是哪一个。
  • 如果有两个副作用修改同一变量,并且都没有在之前排序,则代码具有未定义行为。

  • 如果副作用修改了一个变量,而值计算读取了该变量,并且两个都没有在之前排序,则代码具有未定义行为。

  • 在我看来,这是一个比序列点的旧规则复杂得多的规则,我不是100%肯定我理解对了…无论如何,这一切都归结为,如果要获得值,您需要的副作用已经被应用。

    第一例
    i = ++i;
    

    在这里,要进行赋值,你需要右边部分的值,而要获得这个值,你需要已经应用了副作用;因此,这里的赋值顺序在增量之后,一切都很好。这里重要的一点是,要执行赋值操作,您只需要RHS的值和地址lh。

    回顾一下:

    1. 赋值顺序在&i++i之后
    2. ++i在增量
    3. 之后排序
    4. (传递性)赋值顺序在递增
    5. 之后

    i的值只读一次,在自增之后。它被写入两次,一次是增量操作,一次是赋值操作,但这两个操作是顺序的(先增量操作,再赋值操作)。

    第二种情况
    a[++i] = i;
    

    这里,RHS需要i的值,LHS需要++i的值。然而,这两个表达式没有排序(赋值操作符没有强制排序),因此结果是未定义的。

    回顾一下:

    1. 赋值顺序在&a[++i]i之后
    2. &a[++i]序列在++i
    3. 之后
    4. ++i在增量
    5. 之后排序

    这里i的值被读了两次,一次是赋值的LHS,一次是RHS。LHS部分也进行修改(增量)。然而,赋值操作RHS的写访问和读访问并没有相互排序,因此这个表达式是UB。

    最后咆哮

    让我再说一遍,我不确定我刚才说的话。我强烈的观点是,这种新的前后排序方法更难理解。新规则很有希望只是使一些表达式在UB之前定义得很好(UB是最糟糕的结果),但它也使规则变得更加复杂(它只是"不要在序列点之间改变相同的东西两次"……你不需要做心理拓扑排序来猜测某物是否为UB)。 从某种意义上说,新规则对c++程序没有损害。(UB是敌人,现在这个区域的UB减少了)但是对语言造成了伤害通过增加复杂性(当然c++不需要的东西增加了复杂性)。

    还请注意,++i的有趣之处在于返回值是一个l值(这就是为什么++ ++ i是合法的),所以它基本上是一个地址,逻辑上不需要返回值在增量之后排序。但标准是这样说的,这是你需要燃烧到你的神经元中的规则。当然,要有一个"可用的"++i,您希望值的用户获得更新的值,但就++运算符而言(它返回一个不受增量影响的地址),这种排序不是正式需要

    有了新的规则,你不仅需要做一个心理拓扑排序来看看一个表达式是否有效,而且你还需要使用你只需要记住的任意序列关系来做。

    当然,作为一名程序员,你可能永远不会在没有清晰顺序的情况下编写多次更改相同值的代码,但你仍然会面临其他程序员编写的代码中的错误……当事情不那么清楚的时候,你现在需要更加努力地去理解一些东西是否合法的c++。