【问题标题】:Is the compiler allowed to interlace the evaluation of subexpressions within different function arguments?编译器是否允许在不同的函数参数中交错计算子表达式?
【发布时间】:2016-08-30 23:10:31
【问题描述】:

我想知道以下情况:

void f(int a, int b) { }

int a(int x) { std::cout << "func a" << std::endl; return 1; }
int b(int x) { std::cout << "func b" << std::endl; return 2; }

int x() { std::cout << "func x" << std::endl; return 3; }
int y() { std::cout << "func y" << std::endl; return 4; }

f(a(x()), b(y()));

阅读http://en.cppreference.com/w/cpp/language/eval_order后,我仍然难以理解以下评估顺序是否可行:

x() -> y() -> a() -> b()

或者标准保证a(x())b(y()) 将被评估为单位,可以这么说。

换句话说,有没有可能打印出来

func x
func y
func a
func b

在 GCC 5.4.0 上运行这个测试让我觉得更合乎逻辑

func y
func b
func x
func a

但这当然没有告诉我标准要求什么。如果能获得对该标准的参考,那就太好了。

【问题讨论】:

  • 我继续为您的问题提供了更具描述性的标题。如果您对此有任何问题,请随时更改它。
  • 感谢 chris 和 panta rei 的帮助编辑。
  • 函数arguments的求值与函数的求值无关。关于“作为一个单元进行评估”的部分仅指对函数的评估 - a(...)b(...) 的评估不会交错。

标签: c++


【解决方案1】:

在 C++14 及更早版本中,x -&gt; y -&gt; a -&gt; b 是可能的。这里的排序关系是:

  • x 的调用在调用a 之前进行排序。
  • y 的调用在调用b 之前进行排序。
  • a 的调用在调用f 之前进行排序。
  • b 的调用在调用f 之前进行排序。

订单没有其他限制。如果您想强制执行某些特定的排序,则必须将此调用分解为多个完整表达式。

在 C++14 标准中,注释 [expr.call]/8 阐明了这一意图:

[注意: 后缀表达式和参数的求值都是无序的。参数评估的所有副作用都在输入函数之前排序。 —尾注 ]

如 cmets 中所述,cppreference page 列出了一些标记为“自 C++17 以来”的更多排序规则。这是基于n4606,最新发布的 C++17 草案。所以有可能对于 C++17,这个顺序将不再被允许。

【讨论】:

  • +1 这就是我们使用make_unique&lt;T&gt;(args) 而不是new T(args) 的原因;在后一种情况下,func(unique_ptr&lt;T&gt;(new T(args)), new unique_ptr&lt;U&gt;(new U(args))) 可能会泄漏。
  • @GManNickG 好点,我没有从那个角度考虑过 make_unique
  • 您能否从链接的Order of evaluation 中详细说明这些要点:“A 和 B 的评估是不确定的:它们可以按任何顺序执行,但 可能不重叠:要么 A 在 B 之前完成,要么 B 在 A 之前完成。 [...] 15) 在函数调用中,每个参数的值计算和初始化的副作用是不确定顺序的 关于值计算和任何其他参数的副作用。”。这似乎表明a(x()))b(y()) 可以重叠。
  • @dxiv 在那种情况下,我相信“参数”仅指评估参数表达式的最终结果;这里的“原子”单元是结果和参数之间的转换,它可能与参数具有不同的类型,因此涉及转换序列。
  • cppreference 所基于的当前 C++17 措辞是 5.2.2[expr.call]p5 of n4606
【解决方案2】:

另一种看待它的方式:

在开始评估 a 或 b 之前评估 x 和 y 没有任何好处。事实上,会有一个惩罚。额外的中间结果必须临时保存在某个地方,这要么需要额外的堆栈推送/弹出,要么消耗额外的 CPU 寄存器(过度使用无论如何都会导致额外的堆栈操作)。虽然对于您提供的示例可能没有什么影响或影响很小,但更复杂的案例会显示效率低下。

该规则可以被视为最惰性的评估,即在需要之前不执行评估,以避免携带额外的临时结果。

【讨论】:

  • 嗯 - 有一些好处:大多数编程语言确实从这些表达式中定义了评估顺序,这意味着更少的意外(对于新编码人员和经验丰富的编码人员),并且由于以下原因导致的错误更少评估顺序与编码员的预期不同。此外,在实践中是否有任何证据支持关于效率低下的说法也是值得怀疑的; see this paper,特别是第 7 节结论
  • 我应该认为表达式评估的顺序只与副作用有关,应该避免“最佳实践”,因此无论执行顺序如何,都不会有任何意外。
  • @M.M 事实上,如果你从一个冗长的线程中看到这个 ({groups.google.com/a/isocpp.org/d/msg/std-discussion/7ylid9Tkgp0/…}) 帖子,你会看到一个附加的提案,该提案要求严格的从左到右的评估顺序。
猜你喜欢
  • 1970-01-01
  • 2011-01-04
  • 2018-02-28
  • 1970-01-01
  • 2014-05-30
  • 1970-01-01
  • 1970-01-01
  • 2014-04-09
  • 1970-01-01
相关资源
最近更新 更多