C++ 编译器是否优化了通过值传递的函数参数?

C++ 编译器是否优化了通过值传递的函数参数?,c++,optimization,compiler-construction,C++,Optimization,Compiler Construction,假设我有一个函数,其中参数是通过值而不是常量引用传递的。此外,假设函数内部只使用了值,即函数不尝试修改它。在这种情况下,编译器是否能够计算出它可以通过const引用传递值(出于性能原因),并相应地生成代码?有没有这样做的编译器?只有当函数未导出时,编译器才有可能将“按引用调用”转换为“按值调用”(反之亦然) 否则,由于调用约定,函数必须保持按值/引用语义调用。如果传递变量而不是临时变量,则如果复制构造函数在运行程序时执行任何您可能注意到的操作(“可观察的行为”),则不允许编译器优化复制:输入/输

假设我有一个函数,其中参数是通过值而不是常量引用传递的。此外,假设函数内部只使用了值,即函数不尝试修改它。在这种情况下,编译器是否能够计算出它可以通过const引用传递值(出于性能原因),并相应地生成代码?有没有这样做的编译器?

只有当函数未导出时,编译器才有可能将“按引用调用”转换为“按值调用”(反之亦然)


否则,由于调用约定,函数必须保持按值/引用语义调用。

如果传递变量而不是临时变量,则如果复制构造函数在运行程序时执行任何您可能注意到的操作(“可观察的行为”),则不允许编译器优化复制:输入/输出,或更改可变变量)

除此之外,编译器可以自由地做任何它想做的事情(它只需要类似于可观察的行为,就好像它根本不会优化一样)


只有当参数是右值(大多数是临时的)时,才允许编译器优化by value参数的副本,即使副本构造函数有明显的副作用

这篇文章是对此类优化的极好参考:

对于所有优化,答案通常是“可能”。检查的唯一方法是检查输出程序集,看看它真正在做什么。如果标准允许,它是否真的发生取决于编译器的突发奇想。您不应该依赖它的发生,因为代码库中其他地方的任意更改可能会更改优化器使用的启发式,这可能会导致优化器停止执行特定的优化


安全起见:按照您的意愿编写代码-如果您希望通过引用传递。但是,如果您正在编写可以在任何大小的类型上工作的模板化代码,那么选择就不那么明确了。就我个人而言,我支持按常量引用传递—编译器也可以执行不同的优化,其中可以容纳在引用大小内的小类型是按值传递的,而不是按常量引用传递的。但是,它可能会发生,也可能不会发生。

我不知道有任何常规保证可以做到这一点,但是如果调用的函数是内联的,那么这将允许编译器看到正在进行不必要的复制,如果优化级别足够高,复制操作将被消除。GCC至少可以做到这一点

您可能需要考虑此参数值的类是否具有复制构造函数。如果没有,则传递值和传递常数ref之间的性能差异可能是可忽略的

另一方面,如果类确实有一个复制构造函数来做一些事情,那么您希望的优化可能不会发生,因为编译器无法删除对构造函数的调用——它无法知道构造函数的副作用对您来说并不重要


如果你说参数的类是什么,或者如果它是自定义类,描述它有哪些字段,以及它是否有复制构造函数,那么你可能会得到更有用的答案。

Um,复制不是不受“好像”规则的约束吗?我认为复制是编译器唯一可以消除的,不用担心后果。我相信编译器不需要保证复制构造函数没有“可观察的行为”,因为这与编译器可以从临时文件中删除执行副本的前提相同。@如果从右值复制,sbi复制将被豁免。如果从左值复制,则不是。如果您传递一个右值,并且编译器内联调用函数的代码,它可以优化出它想要的任何东西(事实上,我已经看到GCC通过三到四个嵌套调用将数百个装配线优化为两到三行)。参见C++03规范中的12.8/15@David,该标准明确规定,即使复制构造函数具有可观察的行为,也可以省略复制。第15段读作“当满足某些标准时,允许实现省略类对象的复制构造,即使该对象的复制构造函数和/或析构函数有副作用。”。这就是为什么您可以将print语句放入copy构造函数中,并注意它何时复制,何时不复制。@UncleBens我看不出它在参数传递时这样说。这只适用于返回:
xf(){xx;returnx;}/*可以省略*/
。请注意,如果复制构造函数没有副作用,那么它总是可以省略的,在您的示例中也是如此(就像Asif一样)。我认为您没有抓住本文的重点(至少在这个问题的上下文中是这样)。我明白你的意思,但我认为这篇博文揭示了编译器能够进行的优化,因此我觉得这篇博文并没有完全脱离主题。想想D.Abrahams回答的评论“这是否意味着我们可以完全永远地转储旧的pass-by-const引用和复制操作符=?”,“实际上,是的!昨天转储它。”注意,通常编译器一次只考虑一个TU。实际上,只有在调用方的TU中有函数定义和/或链接时间优化的情况下,调用方才能根据被调用方的情况做一些不同的优化。相关问题:当然可以优化它的内联副本,即使函数的外部可见版本不是。