C ++ 17之前的函数交织

时间:2019-05-24 11:36:35

标签: c++ c++11 c++14 language-lawyer c++17

看看这个简单的函数调用:

f(a(), b());

根据标准,未指定a()b()的呼叫顺序。 C ++ 17具有附加规则,该规则不允许将a()b()进行交错。据我所知,在C ++ 17之前还没有这样的规则。

现在,请看下面的简单代码:

int v = 0;

int fn() {
    int t = v+1;
    v = t;
    return 0;
}

void foo(int, int) { }

int main() {
    foo(fn(), fn());
}

使用C ++ 17规则,在调用v之后,2的值肯定为foo。但是,这使我想知道,对于C ++ 17之前的版本,是否可以保证相同?还是v1结尾?如果不是int t = v+1; v = t;,而只有v++,会有所作为吗?

4 个答案:

答案 0 :(得分:6)

在以前的版本中,函数调用也不允许插入。

引用C ++ 11最终草案(n3337)

  

1.9程序执行[intro.execution]
  ...

     

15。 ...   在调用函数时(无论函数是否为内联),与任何参数表达式或指定所调用函数的后缀表达式相关联的每个值计算和副作用都将在执行主体中的每个表达式或语句之前进行排序。称为函数。 [注意:与不同参数表达式关联的值计算和副作用是无序列的。 —尾注]   相对于被执行函数 9 的执行,不确定地在执行被调用函数的主体之前或之后未按顺序对调用函数中的每个评估(包括其他函数调用)进行排序。 。


  

9)换句话说,函数执行不会相互交织

类似的措辞也可以在C ++ 14版本的最终草案中找到。

答案 1 :(得分:2)

v在所有版本的C ++(和C)中必须为2。函数fn()必须执行两次,显然每次执行时,函数v都会递增。这里没有多线程,没有数据争用,也没有fn()仅被部分执行然后在fn()的另一次调用继续进行时被中断的可能性。

答案 2 :(得分:2)

  

C ++ 17具有附加规则,该规则不允许对a()和b()进行交织。据我所知,在C ++ 17之前还没有这样的规则。

尽管措辞和一些细节变得更加准确,但这里还是有一些规则适用。

C ++ 03 [intro.execution] / 8:

  

一旦函数开始执行,在被调用函数执行完成之前,不会评估调用函数的任何表达式。 [脚注8]

     

[脚注8]:换句话说,函数执行不会相互交错。

尽管您可能会争辩说这实际上并没有说明从文本中的调用函数调用的其他函数,而且脚注在官方上不是规范性的。

C ++ 11更改了措辞,主要是因为它引入了多线程语义。 C ++ 11和C ++ 14 [intro.execution] / 15,重点是我的:

  

在调用一个函数时(无论该函数是否是内联的),与任何参数表达式或指定所调用函数的后缀表达式关联的每个值计算和副作用都将在执行每个表达式或语句之前进行排序。被调用函数的主体。 [注意:与不同参数表达式关联的值计算和副作用未排序。 -尾注] 对于执行函数主体之前或之后执行的未确定顺序排序的调用函数中的每个评估(包括其他函数调用),都不确定 [脚注9]

     

[脚注9]换句话说,函数执行不会相互交织。

至少在大多数情况下,我认为这句话毫无疑问。

C ++ 17 [intro.execution] / 18:

  

在调用一个函数时(无论该函数是否是内联的),与任何参数表达式或指定所调用函数的后缀表达式关联的每个值计算和副作用都将在执行每个表达式或语句之前进行排序。被调用函数的主体。对于每个函数调用 F ,对于在 F 中发生的每个评估 A 和未发生的每个评估 B F 中,但在同一线程上并且作为同一信号处理程序(如果有)的一部分进行评估, A 会在 B 之前排序,或者 B A 之前排序。 [脚注10] [注意:如果 A B 否则不进行排序,则它们将不确定地排序。 -尾注]

     

[脚注10]换句话说,函数执行不会相互交织。

这是关于单独函数中所有求值的更通用的说明,而不仅仅是函数调用中的参数。但据我所知,这种更精确的措辞只是澄清了一些可能模棱两可的情况,但并没有真正改变语义行为。

答案 3 :(得分:0)

我怀疑我们会混淆两个概念。调用顺序仅在c ++ 17中的表达式中固定。始终不允许在函数的两次调用之间对函数中的语句进行交织,尽管当优化器掌握代码后,只能保证效果。

如果您写a() ; b(),则在b被完全执行之前,a()将被完全执行。如果您写a(), b()a() && b()

,则相同

如果您编写a() + b(),则在c ++ 17之前不能保证a()将在b()之前执行,但是可以保证先执行的那个将在完成之前完全完成。其他被执行。

如果您写c(a(), b()),则[我的理解]是,不能保证a()将在b()之前执行,但是/ is /可以保证第一个执行函数的效果(无论哪种方法)都将在第二个方法开始之前完成,当然可以保证c()在a()和b()都完成之前不会执行。

优化器应该尊重该保证的意图,尽管它可能会采用a()和b()甚至c()的语句,然后将它们相互混合,从而产生运行的效果。代码应与按顺序运行一样。 -O0代码可以先执行a()然后b(),-O3代码先执行b()然后a()吗?也许,尽管那样会使调试变得非常困难,但我希望不会。

编译器只需要在最终结果中发挥作用,并且在多线程处理中,另一个线程可能会观察到连续代码行的影响是乱序发生的,除非使用了特定的线程感知结构。

据我了解,优化程序不能使a(),b()和c()的特定效果似乎在每个函数之间乱序发生,尽管在c ++ 17之前a()和b()的定义不明确-b()的所有作用可能先于a()的所有作用发生。