编译器是否决定何时内联我的函数(在C ++中)?

13

我知道你可以使用inline关键字,或者像短构造函数(short ctor)或getter方法一样将方法放在类声明中,但编译器是否会最终决定何时内联我的方法?

例如:

inline void Foo::vLongBar()
{
   //several function calls and lines of code
}

如果编译器认为将我的代码变得低效,那么它会忽略我的内联声明吗?

另外一个问题是,如果我像这样在类外面声明一个getter方法:

void Foo::bar() { std::cout << "baz"; }

编译器会在底层将此内联吗?

9个回答

16

1
你能详细说明一下为什么吗?我认为 C++ 的目的是给程序员足够的自由度,但这似乎有些限制。有什么好的理由吗? - jkeys
1
@Hooked,请看我发的链接。它详细解释了为什么这并不总是好事。我首先想到的是头递归函数。 - JaredPar
1
标准规定,即使__forceinline不能结束争论,编译器仍然有最后的决定权。我可以理解何时内联是不合适的,但如果程序员/分析工具决定覆盖编译器,他有哪些选择?该链接未详细说明让编译器决定的原因。 - jkeys
2
可能无法将某个给定的函数内联。尽管如此,应该注意到,至少有些编译器仍然提供了一种覆盖最终决策的方法 - 例如 VC++ 中的 __forceinline。这仍然不能让您内联不可能内联的函数,但它将覆盖编译器的成本/效益分析。 - Pavel Minaev

12

无论一个函数是否被内联,最终都完全取决于编译器。通常,一个函数在流程方面越复杂,编译器内联的可能性就越小。而一些函数,例如递归函数,根本无法被内联。

不内联函数的主要原因是它会大大增加代码的总体大小,从而阻止其被保存在处理器的缓存中。这实际上是一种恶化,而不是优化。

至于让程序员自己决定是否自掘坟墓,你可以自己内联函数-编写本应该放在函数调用点的代码。


请看一下我对JaredPar的回答的评论。 - jkeys
2
递归函数可以内联到指定深度 :) - Khaled Alshaya
2
编译器在编译时如何知道深度会是多少? - anon
@Neil Butterworth:我认为如果迭代次数是常量或constexpr,编译器可以看到有多少次迭代。 - the_drow
一个函数可以递归地内联到任意深度,并在此之后调用一个非内联版本。 - geon
显示剩余3条评论

5
正如其他人所指出的那样,inline关键字只是向编译器建议将代码内联。由于编译器通常会内联未标记为inline的代码,并且不会内联已标记为inline的代码,因此该关键字似乎与register或(C++0x之前的)auto一样多余。
然而,inline关键字还有一个影响:它将函数的链接性从默认的外部更改为内联。内联链接允许每个编译单元包含自己的对象代码,并且使链接器从最终可执行文件中删除冗余副本。如果这让您想起了模板,那么是的,模板也使用内联链接。

4

正如许多人已经发表的意见一样,最终的决定始终取决于编译器,即使您可以给出像forceinline这样的坚定提示。

部分理由是内联不是自动的“加速”开关。过多的内联可能会使您的代码变得更大,并且可能会干扰其他优化。请参见C ++ FAQ Lite有关内联函数和性能的文章


谢谢提供链接。我已经阅读了大部分的 Faq lite,但还没有看过这个。 - jkeys

3

3

只是想补充一下我的看法...

我发现这篇Guru of Week关于内联的文章非常有用。

据我所知,即使链接器在链接目标文件时发现可以进行内联,它也可能会进行内联。


2
作为一个附带问题,如果我在类外声明了一个getter方法,就像这样:
```java public String getName() { return this.name; } ```
那么我应该如何在我的代码中调用它呢?
void Foo::bar() { std::cout << "baz"; }

编译器会在底层内联这个函数吗? 答案是取决于情况的。对于同一翻译单元(即.cpp文件及其所有#included定义)中的所有调用者,编译器可以进行内联。但它仍然必须编译一个非内联版本,因为可能有来自翻译单元之外的函数调用者。如果您的编译器实际上可以执行此操作,则可以在高优化级别下看到其效果。(特别是:将所有定义放在一个翻译单元中,与典型布局相比,这种内联的机会会大大增加。)

哦,我不知道那个。我不知道链接器到底做了什么(我的高级 IDE 让我太过依赖,我从未使用命令行编译并经历过这个流程)。 - jkeys
了解链接器的工作方式应该是相当透明的(你不需要知道它才能完成工作)。但在C++中,学习它变得尤为重要,因为许多语言结构的行为方式如果你不知道链接器可见的内容和不可见的内容,就会有所不同。其中“inline”是最大的一个,但是什么放在“.h”和“.cpp”中的区别、静态含义、模板可以放到哪里等等都会出现这种情况。不幸的是,如果您对此没有基本的了解,链接可能会在C++中给您带来很大的困扰。 - quark

1
根据我的了解,如果编译器发现像 for,while 等循环语句,它会自动将你声明为内联的函数(或写在类声明内部的函数)更改为非内联。 这是编译器对内联函数有决定权的一个例子。

我...你有什么来源来支持那个说法吗? - jkeys
1
循环?为什么是循环?你不是指递归吗? - Vilx-
这是具体实现相关的。标准并没有说明函数何时可能不会被内联,只有实现方才有最终决定权。 - Pavel Minaev

1
如果您确实、坚决、绝对不容失败地需要内联代码,那么总有宏的方法。 C语言多年以来一直支持这些宏,因为它们在编译之前就只是文本替换,所以无论您写什么,它们都真正地内联了。
这就是为什么 'inline'关键字(甚至在某些情况下是强制变体)可以不必有一个强制的标准方式的原因——您总是可以编写一个宏。
话虽如此,inline关键字通常更好,因为编译器很多时候知道是否将函数内联化是有意义的,而且因为inline可以与其他编译器优化相互作用。

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