C ++旨在为程序员提供选择,即使这使得程序员可以选择错误。
如果以这种方式设计,为什么没有标准方法强制编译器内联某些东西,即使我可能错了?
或者我可以问为什么inline
关键字只是一个提示?
我想我在这里别无选择。
在OOP世界中,我们调用对象上的方法,并且应该避免直接访问成员。如果我们不能强制访问内联,那么我们就无法编写高性能但仍可维护的应用程序。
(我知道很多编译器都用自己的方式来强制内联,但它很难看。使用宏来创建类的内联访问器也很难看。)
编译器总是比程序员做得好吗?
答案 0 :(得分:18)
编译器如何内联递归函数(特别是如果编译器不支持Tail-call优化,即使它支持Tail-call optimize-able)。
这只是编译器应该决定内联是否可行的一个原因。我现在也可以想到其他人。
答案 1 :(得分:5)
编译器总是比程序员做得好吗?
不,并非总是......但程序员更容易出错,并且不太可能在几年内保持最佳调整。最重要的是,如果函数非常小(对于至少一个公共/重要的代码路径),内联仅有助于性能,但是当然,它可以帮助大约一个数量级,这取决于许多事情。程序员评估通常是不切实际的,更不用说仔细考虑一个函数是多么微不足道了,阈值可能因编译器实现选择,命令行选项,CPU模型等而有所不同。有很多东西可能会突然膨胀function - 任何非内置类型都可以触发各种不同的行为(特别是模板),运算符的使用(偶数new
)可以重载,调用约定和异常处理步骤的详细程度一般不会对程序员来说很明显。
如果编译器没有内联的内容足够小,以至于如果它被内联就会期望有用的性能提升,那么编译器可能会意识到某些实现问题而不是实际上会让它变得更糟。在那些灰色的情况下,编译器可能会采用任何一种方式,而你只是超过某个阈值,性能差异无论如何都不太可能显着。
此外,一些程序员(包括我自己)可能是懒惰的并且故意滥用inline
作为将实现放入头文件,绕过ODR,的方便方法,即使他们知道这些函数很大,如果编译器(被要求)实际内联它们将是灾难性的。这并不排除强制内联关键字/表示法......它只是解释了为什么很难改变当前inline
关键字的期望。
答案 2 :(得分:3)
或者我可以问为什么内联关键字是 只是一个提示?
因为你“可能”比编译器更了解。
大多数情况下,对于未标记为内联的函数(以及正确声明/定义的),编译器将根据其配置和实现自行评估函数是否可以内联。
例如,如果代码不长和/或太复杂,大多数编译器将自动内联在标题中完全定义的成员函数。那是因为由于函数在标题中可用,为什么不尽可能多地内联它呢? 但是这不会发生,例如,在Visual Studio的调试模式下:在调试中,调试信息仍然需要映射函数的二进制代码,因此它避免内联,但仍会内联标记为内联的函数,因为用户需要它。如果你想标记函数,那么这很有用,你不需要有调试时信息(比如简单的getter),同时在调试时获得更好的性能。 在发布模式下(默认情况下),编译器会自动内联所有内容,即使激活调试信息,也很难调试代码的某些部分。
因此,一般的想法是,如果您以一种有助于编译器内联的方式进行编码,它将尽可能地内联。如果您以难以或无法内联的方式编写代码,则会避免。如果你用内联标记某些内容,你只需告诉编译器,如果它发现它很难但不是不可能内联,它应该内联它。
由于内联取决于调用者和被调用者的上下文,因此没有“规则”。 通常建议的是简单地忽略明确标记函数内联,但在两种情况下:
编译器是否总是做得更好 比程序员?
不,这就是为什么有时使用inline关键字可以提供帮助。程序员有时可以比编译器更好地了解所需的内容。例如,如果程序员希望它的二进制文件尽可能小,那么根据代码,内联可能是有害的。在速度性能要求的应用中,积极地内联可以非常有用。编译器如何知道需要什么?它必须进行配置,并允许以细粒度的方式了解真正想要内联的内容。
答案 3 :(得分:0)
错误的假设。
是的一种方式。它拼写为#define
。对于许多早期的C项目来说,这已经足够了。 inline
是完全不同的 - 提示,更好的语义 - 它可以添加到宏之外。但是一旦你同时拥有这两个选项,中间几乎没有留下第三个选项的空间,一个具有更好的语义,但是不可选。
答案 4 :(得分:-1)
如果你真的需要强制函数的内联(为什么?),你可以这样做:复制代码并粘贴它,或使用宏。