3
#include <iostream>
int foo() {
    std::cout<<"foo() is called\n";
    return 9;
}
int bar() {
    std::cout<<"bar() is called\n";
    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 等语言中严格指定。

4

4 回答 4

9

我认为问题的整个前提是错误的:

不指定函数参数的准确求值顺序如何帮助 C & C++ 编译器生成优化代码?

这与优化代码无关(尽管它确实允许这样做)。它是关于不惩罚编译器,因为底层硬件具有某些 ABI 约束。

一些系统依赖于以相反顺序推送到堆栈的参数,而另一些则依赖于正向顺序。C++ 可以在各种具有各种约束的系统上运行。如果您在语言级别执行命令,您将需要某些系统支付罚金以执行该命令。

C++ 的第一条规则是“如果你不使用它,那么你不应该为它付费”。因此,执行命令将违反 C++ 的主要指令。

于 2016-05-04T14:34:05.490 回答
7

它没有。至少,今天没有。也许过去是这样。

C++17的提案建议为函数调用定义左右评估顺序,operator<<依此类推。

如该论文第 7 节所述,该提议通过编译 Windows NT 内核进行了测试,它实际上导致了某些基准测试的速度提高。作者的评论:

值得注意的是,这些结果是针对优化器尚未更新以意识到的最坏情况,并利用新的评估规则,他们被迫盲目地从左到右评估函数调用。

表明速度还有进一步提升的空间。

于 2016-05-04T14:42:38.237 回答
5

评估的顺序与参数传递的方式有关。如果堆栈用于传递参数,则从右到左评估有助于提高性能,因为这是将参数推入堆栈的方式。

例如,使用以下代码:

void foo(bar(), baz());

假设调用约定是“通过堆栈传递参数”,C 调用约定要求从最后一个开始将参数推入堆栈 - 这样当被调用函数读取它时,它会首先弹出第一个参数并能够支持可变参数函数. 如果评估顺序是从左到右,bar()则必须将结果保存在临时中,baz()然后调用,它是结果推送,然后是临时推送。但是,从右到左的评估允许编译器避免临时的。

如果参数是通过寄存器传递的,那么评估的顺序并不是很重要。

于 2016-05-04T13:56:41.277 回答
2

C 和 C++ 标准没有指定函数参数的评估顺序的最初原因是为编译器提供更多优化机会。不幸的是,在最初设计这些语言时,这种基本原理还没有得到广泛实验的支持。但这是有道理的。

这个问题在过去几年中被提出。请参阅Herb Sutter 的这篇博文,不要忘记浏览评论。

提案P0145R1建议最好为函数参数和其他运算符指定评估顺序。它说:

标准中当前指定的表达式求值顺序破坏了建议、流行的编程习惯或标准库设施的相对安全性。陷阱不仅适用于新手或粗心的程序员。即使我们知道规则,它们也会不分青红皂白地影响我们所有人。

您可以在该文档中找到有关这如何影响优化机会的更多信息。

在过去的几个月里,关于语言的这种变化如何影响优化、兼容性和可移植性,已经进行了非常广泛的讨论。线程从这里开始并在这里继续。你可以在那里找到很多例子。

于 2016-05-11T07:24:53.587 回答