#include <iostream>
int foo() {
std::cout<<"foo() is calledn";
return 9;
}
int bar() {
std::cout<<"bar() is calledn";
return 18;
}
int main() {
std::cout<<foo()<<' '<<bar()<<' '<<'n';
}
// Above program's behaviour is unspecified
// clang++ evaluates function arguments from left to right: http://melpon.org/wandbox/permlink/STnvMm1YVrrSRSsB
// g++ & MSVC++ evaluates function arguments from right to left
// so either foo() or bar() can be called first depending upon compiler.
上述程序的输出取决于编译器。未指定函数参数的计算顺序。我读到这个的原因是它可以产生高度优化的代码。如何不指定函数参数的精确计算顺序,以帮助编译器生成优化的代码?
AFAIK,评估顺序在Java,C#,D等语言中严格指定。
我认为这个问题的整个前提是错误的:
如何不指定函数参数的精确求值顺序有助于C&C++编译器生成优化的代码?
这不是关于优化代码(尽管它确实允许这样做(。这是关于不惩罚编译器,因为底层硬件具有某些 ABI 约束。
一些系统依赖于以相反顺序推送到堆栈的参数,而其他系统则依赖于前向顺序。C++运行在具有各种约束的各种系统上。如果您在语言级别执行命令,您将要求某些系统支付罚款才能执行该命令。
C++的第一条规则是"如果你不使用它,那么你就不必为此付费"。因此,执行命令将违反C++的主要指令。
它没有。至少,今天没有。也许过去确实如此。
C++17 的提案建议定义函数调用、operator<<
等的左右求值顺序。
如该论文第7节所述,该提案通过编译Windows NT内核进行了测试,它实际上导致了某些基准测试的速度提高。 作者评语:
值得注意的是,这些结果是针对最坏情况的,即优化器尚未更新以了解,并利用新的评估规则,盲目地被迫从左到右评估函数调用。
表明速度还有进一步提高的空间。
计算顺序与参数的传递方式有关。如果使用堆栈传递参数,则从右到左计算有助于提高性能,因为这是将参数推送到堆栈中的方式。
例如,使用以下代码:
void foo(bar(), baz());
假设调用 conevention 是"通过堆栈传递参数",C 调用约定要求从最后一个参数开始将参数推入堆栈 - 这样当被调用方函数读取它时,它会首先弹出第一个参数并能够支持可变参数函数。如果评估顺序是从左到右,则必须将bar()
的结果保存在临时中,而不是baz()
调用的结果,它是结果推送,然后是临时推送。但是,从右到左的计算允许编译器避免临时计算。
如果参数通过寄存器传递,则计算顺序并不过分重要。
C 和 C++ 标准没有指定函数参数的计算顺序的最初原因是为编译器提供更多的优化机会。不幸的是,在最初设计这些语言时,这一基本原理并未得到广泛实验的支持。但这是有道理的。
这个问题在过去几年中已经提出。请参阅Herb Sutter的这篇博客文章,不要忘记浏览评论。
提案 P0145R1 建议最好为函数参数和其他运算符指定计算顺序。它说:
表达式求值的顺序,如 它目前在标准中指定,破坏建议, 流行的编程习语,或标准库的相对安全性 设施。 陷阱不仅适用于新手或粗心大意的人 程序员。 它们不分青红皂白地影响着我们所有人,即使我们知道 规则。
您可以在该文档中找到有关这如何影响优化机会的更多信息。
在过去的几个月里,关于语言的这种变化如何影响优化、兼容性和可移植性进行了非常广泛的讨论。线程从这里开始,从这里继续。你可以在那里找到很多例子。