编译器是否允许在不同的函数参数内交错计算子表达式

Is the compiler allowed to interlace the evaluation of subexpressions within different function arguments?

本文关键字:参数 计算 表达式 函数 是否 编译器      更新时间:2023-10-16

我想知道以下情况:

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

,但这当然没有告诉我任何关于标准要求的东西。如果能得到标准的参考就好了。

在c++ 14及更早的版本中,x -> y -> a -> b是可能的。这里的排序关系为:

  • 呼叫x在呼叫a之前排序。
  • 呼叫y在呼叫b之前排序。
  • 呼叫a在呼叫f之前排序。
  • 呼叫b在呼叫f之前排序。

订单上没有其他限制。如果你想执行一些特定的排序,那么你必须将这个调用分解成多个完整表达式。

在c++ 14标准中,[expr.call]/8: 说明了这个意图。

[注意:后缀表达式的求值和参数的求值都是相对于彼此不排序的。在进入函数之前,对参数求值的所有副作用进行排序。- 结束说明]

如注释中所述,cppreference页面列出了一些标记为"自c++ 17以来"的排序规则。这是基于n4606, c++ 17最新发布的草案。因此,在c++ 17中,这个顺序可能不再被允许。

从另一个角度来看:

在开始计算a或b之前同时求值x和y是没有好处的。事实上,会有惩罚。额外的中间结果必须临时保存在某个地方,这可能需要额外的堆栈push/pop,或者消耗额外的CPU寄存器(过度使用会导致额外的堆栈操作)。虽然这对于您提供的示例来说可能没有什么影响,但更复杂的情况会揭示效率低下。

该规则可以被视为最懒惰的求值,即直到需要时才执行求值,以避免携带额外的临时结果。