2

考虑以下代码:

int main(){
  int i = 0;
  int a = ++i + ++i;
}

我找不到任何说明 的操作数+未排序的信息。所以按照标准,二进制操作数的顺序+是不定序的。

[介绍、执行]/15

给定任意两个评估 A 和 B,如果 A 在 B 之前排序(或者,等效地,B 在 A 之后排序),则 A 的执行应先于 B 的执行。如果 A 没有在 B 之前排序并且 B 没有排序在 A 之前,然后 A 和 B 是无序的。[注意:未排序评估的执行可以重叠。——尾注]

当 A 在 B 之前排序或 B 在 A 之前排序时,评估 A 和 B 的排序不确定,但未指定哪个。[注意:不确定排序的评估不能重叠,但可以先执行。——尾注]

引用的意思是 A 的评估可以在 B 之前发生,或者 B 的评估可以在 A 之前发生。并且未排序的评估的执行可以重叠,而不确定序列的评估不能重叠,这是不同的。

我们知道 的 修改i总是发生在i由于 prefix的值计算之前++

然后按照规则:

表达式(或子表达式)的评估通常包括值计算(包括确定对象的身份以进行右值评估和获取先前分配给对象以进行纯右值评估)和副作用的启动

如果内存位置的副作用相对于同一内存位置的另一个副作用或使用同一内存位置中任何对象的值的值计算是无序的,并且它们不是潜在的并发,则行为未定义

因此,无论 A 的评估是在 B 之前还是相反,都没有与相应值计算相关的副作用或++i + ++i;. 因为不确定顺序的评估不能重叠,所以两个评估之一必须在另一个之前完全执行。评估包括价值计算和副作用。因此,一个增量 to在另一个增量i之前被评估。

然而,无序的求值遵循不同的规则,所以如果二进制操作数的求值+无序的而不是不确定的,那么混淆就会得到解决。如果我在上面的分析中遗漏了标准中的某些内容,请纠正我。

更新

我找到了下面这句话,这似乎表明评估是无序的:

除非另有说明,否则单个运算符的操作数和单个表达式的子表达式的求值是未排序的。

但是,我不知道如何正确理解这句话。我想出了两种解释:

对于算子 A,A 的操作数的求值是无序的;对于表达式 B,B 的子表达式的计算彼此之间是无序的。

将单个运算符的操作数的求值视为 A。将单个表达式的子表达式的求值视为 B。A 与 B 无序。

哪种解释是正确的?

4

2 回答 2

3

标准文本似乎1暗示行为未定义。

  • <a>+<b>评估中<a><b>未排序2,3
  • 这两个部分具有影响相同内存位置的副作用

(1) 在我看来,这部分是明确和清晰的,但我不确定是否有其他部分说相反,或者某些更高级别的概念(例如什么是程序的执行)在逻辑上没有被破坏,因为的相互矛盾的规则。考虑到 C++ 的复杂性,我实际上会对没有出现错误感到非常惊讶。

(2) 在重载的情况下,operator+它们将被不确定地排序(因为规则与函数调用相同,因此不是未定义的行为:N4713 的 8.5.1.2[5] 说“后缀表达式在每个表达式之前排序在表达式列表和任何默认参数中。参数的初始化,包括每个关联的值计算和副作用,相对于任何其他参数的顺序是不确定的”)但是对于 native ints 这并不适用,并且行为是不明确的。

(3) 正文说“除非另有说明,对单个运算符的操作数和单个表达式的子表达式的求值是无序的”。当然,对于一元运算符,这个问题是无关紧要的(没有顺序可谈),对于三元?:运算符,有特殊的排序规则。关于“子表达式”的部分将涵盖诸如a[++i][++i]where ais 例如 a 的char **情况:在这种情况下,两个相同的子表达式++i是未排序的,并且具有修改相同内存位置和未定义行为的副作用。我认为该段落实际上比必要的更复杂,因为运算符的操作数也是表达式的子表达式,因此最后一部分就足够了。

于 2020-04-17T07:29:45.463 回答
1

让我给出一个答案,使问题的答案更清楚。首先,考虑以下句子

除非另有说明,否则单个运算符的操作数和单个表达式的子表达式的求值是无序的。

句子可以分成两部分

1. 除非另有说明,对单个运算符的操作数的评估是无序的。
2. 除非另有说明,对单个表达式的子表达式的评估是无序的。

那么,第 1 部分是什么意思?这意味着一个集合X由一个运算符的这些操作数组成,并且集合的每个元素X彼此之间是无序的,它们是无序的。句子 2 类似于句子 1,它只是使集合X由表达式的这些子表达式组成。

二元运算符的那些语句+在 [expr.additive] 中有描述,这些与该部分中的操作数序列无关,因此,1在运算符上执行的规则。因此,正如标准所说的那样,不序列可以重叠,这是什么意思?意味着“每个评估的部分甚至可以交错在一起”(@Ben Voigt 在评论中说)。所以,++i + ++i通常情况下:

如果内存位置上的副作用相对于同一内存位置上的另一个副作用或使用同一内存位置中任何对象的值的值计算是未排序的,并且它们不是潜在的并发,则行为未定义

所以,int a = ++i + ++i是未定义的行为,这个问题的关键是理解下面的句子:

除非另有说明,否则单个运算符的操作数和单个表达式的子表达式的求值是无序的。

于 2020-04-20T03:11:29.257 回答