函数参数的评估顺序是什么?

117
如果我们有三个函数(foo,bar和baz),它们的组合如下...
foo(bar(), baz())

C++标准有没有保证在baz之前bar会被评估?
6个回答

126

没有这种保证。根据C++标准,这是未指定的。

Bjarne Stroustrup在《C++程序设计语言》第三版6.2.2节中明确表示了这一点,并进行了一些推理:

在表达式求值顺序没有限制的情况下,可以生成更好的代码

尽管技术上这是指同一部分中较早的一个部分,该部分指出表达式的各个部分的求值顺序也是未指定的,即

int x = f(2) + g(3);   // unspecified whether f() or g() is called first

9
如果表达式的求值顺序是严格的,那么可以编写更好的代码(更干净),这通常比代码生成更重要。请参考此示例:http://stackoverflow.com/questions/43612592/the-case-where-c-ab-is-not-equal-to-c-ab-b-b1?noredirect=1#comment74274141_43612592。所以,Stroustrup就在这里。 - Bill Kotsias
5
如果顺序很重要,你可以自己进行排序。否则,为了一些并不总是(很少?)重要的东西而付出代价是不划算的。我认为不为未使用的东西付费的政策是大多数C++程序员都同意的唯一事情。 - tweej
4
应该使用“未指定行为”而不是“未定义”的术语吗? - GoodDeeds
1
在C++17之前,如果函数在同一内存位置上引起副作用,则会导致未定义的行为。在C++17之后,这是未指定的。 - Passer By
8
@ChrisDodd因使用“undefined”而不是“unspecified”对已接受的答案进行投票否决,这让我感到像是恶意的吹毛求疵...我没有说这是“未定义行为”,而且“undefined”和“unspecified”似乎是同义词?无论如何,建议对答案进行编辑将是一种更有成效的讨论方式。 - Eli Bendersky
显示剩余3条评论

28

来自[5.2.2]函数调用:

参数的求值顺序是未指定的。所有参数表达式的副作用在进入函数之前都会生效。

因此,不能保证bar()baz()之前运行,只能确保在foo之前调用bar()baz()

还需要注意来自[5]表达式的:

除了特殊说明(例如&&||的特殊规则),个别运算符和表达式子表达式的求值顺序以及副作用发生的顺序均未指定。

因此,即使您问foo(bar()+baz())中的bar()是否会先于baz()运行,其顺序仍然未指定。


4
[5.14] 逻辑 AND 运算符的“特别说明”示例:“与&不同,&&保证从左到右进行求值:如果第一个操作数为 false,则不会对第二个操作数进行求值。” - Daniel Trebbien

21
没有指定bar()和baz()的顺序,标准唯一规定的是它们都会在调用foo()之前被评估。来自C++标准,第5.2.2/8节:

参数求值顺序是未指定的。


5
在foo()函数之前对它们进行评估这一事实,至少有点令人放心。 - Bill Kotsias
2
@BillKotsias 标准还规定了函数调用不能重叠(即实现不能运行 bar 的第1行,然后是 baz 的第1行,接着是 bar 的第2行等),这也很不错。 :-) - melpomene

17

3
在C++11中,相关文本可以在8.3.6默认参数/9中找到(强调是我的)。
默认参数每次调用函数时都会被评估。函数参数的评估顺序是未指定的。因此,函数的参数不应该在默认参数中使用,即使它们没有被评估。
C++14标准也使用相同的措辞,并在同一章节下找到。

-1

正如其他人已经指出的那样,标准并没有针对这种情况提供任何关于评估顺序的指导。评估顺序留给编译器处理,而编译器可能会有保证。

重要的是要记住,C++标准实际上是一种指导编译器构建汇编/机器代码的语言。标准只是方程式的一部分。当标准存在歧义或特别是实现定义时,您应该向编译器寻求帮助,并了解它如何将C++指令转换为真正的机器语言。

因此,如果评估顺序是要求,或至少很重要,并且跨编译器兼容性不是要求,请探究您的编译器最终如何将其组合在一起,您的答案可能就在那里。请注意,编译器将来可能会改变其方法。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接