源文件中的内联

6

我有一个包含许多实用函数的大类。这些函数非常小,我希望将它们内联。

问题在于它们都在一个源文件中,并且应该保留在源文件中而不是移动到头文件中(这样每次更改时我就不需要重新编译所有内容)。

如果我将它们标记为inline,我会得到“symbols not found”的错误。

是否有一种方法可以使它们内联,或者我需要盲目地信任链接时间优化器?
我需要代码在clang 3和gcc 4.6之间可移植,但是基于编译器的#define也可以(因此只回答如何在其中一个编译器中完成即可)。


5
所以,基本上你想要魔法。 - R. Martinho Fernandes
@R.MartinhoFernandes:不,我想要一个可以复制粘贴的链接器。 - Daniel
@Dani:那就是长期资本增值税/长期持有税。 - Cat Plus Plus
@Dani:复制粘贴不是内联的主要目的。 - iehrlich
5个回答

6
将实现放在一个 .inl 文件中。只有必要的 .cpp 文件需要 #include 它。这样,对实现的更改(触及 .inl 文件)仅会触发依赖的 .cpp 文件重新编译。对定义的更改(触摸 .h 文件)会触发所有使用声明的文件重新编译。
这是内联函数和模板实现的常见做法,.inl 文件基本上应该被视为“包含的 cpp”文件。

这不是坏建议,但它对编译时成本的帮助不大。我想你可以为每个函数拥有一个.inl文件,但在我看来,这将会增加更多的维护负担而不值得。 - zwol

5
这些函数非常小,我希望它们被内联。但是我不想每次更改时都重新编译所有内容。这两个要求是互相冲突的。如果一个函数被内联了,那么当该函数发生变化时,你别无选择,只能重新编译所有调用它的程序。这就是内联的工作原理。即使你使用链接时优化器在链接时自动完成,你仍将支付重新处理所有调用者的编译时间成本。据我所知,无论是gcc 4.6还是clang 3都没有达到标准的链接时优化器。此外,没有我知道的编译器有足够好的启发式算法来使手动inline注释不必要。即使是我在评论中提到的VS2010作为一个良好的链接时优化器,仍然需要很多关于何时内联的建议。

由于链接器可以修改文件中的符号位置,我相信它可以根据指令将一个符号粘贴在另一个符号中间。 - Daniel
@Dani:但这并不能真正加快编译速度,对吧?每次这些内容发生变化时,它仍然需要在每个调用者上执行相应的工作。 - R. Martinho Fernandes
那样做几乎无法获得内联的全部好处;内联是在编译管道的早期完成的,以便随后的阶段可以消除不必要的条件语句并在调用方和被调用方之间广泛地重新排列代码。此外,我不知道有任何链接器可以这样做。这将几乎需要与真正的链接时优化器相同的工作量,但收益不到10%(我估计)。如果您想了解非LTO链接器执行的非常有限的优化集,请搜索“链接器松弛”;它往往是像替换较短的分支序列之类的东西。 - zwol
GCC 4.6确实有一个可用的LTO,从意义上讲它不会崩溃或生成错误代码,但是如果你将其输出与例如带有配置反馈的Visual Studio 2010进行比较(顺便说一下,这是我所知道的唯一值得使用的LTO编译器),你会发现它还有很长的路要走。 - zwol

4
如果您希望一个函数被内联,除非它只在同一源文件中使用,否则必须将其放在头文件中。原因是编译器需要实际的函数定义才能在调用处“内联”定义,并进行编译。
您可以在这里找到更多信息:这里这里

2
您需要在类定义中使用必须内联的函数。

-1 是因为在类定义内部定义的函数并不是强制性的内联函数;它们与使用 inline 关键字在类定义外部定义的函数完全相同。而你对优化问题的回答却涉及语法,这表明你没有理解问题。(你的回答表明你理解了问题,但从你所说的话中并不清楚。) - zwol
Zack说:“因为在类定义内部定义的函数不是强制性的内联函数” - 我猜这取决于编译器,因为我知道至少有一个编译器符合我所说的。 - iehrlich
1
@suddnely_me 顺便问一下,那是什么编译器?如果我在类定义中写一个100行的函数会怎样?会不会让我的可执行文件变得像18轮卡车一样臃肿? - R. Martinho Fernandes
那个编译器是否也将inline视为强制性的,而不是提示?有些编译器没有任何启发式方法来告诉它们何时内联程序员认为应该内联的内容。 - zwol
@R. Martinho Fernandes:他们说是ICC。我不太确定,但我将在几个小时后上班,可以问问我们的开发人员。 - iehrlich
显示剩余2条评论

1

如果函数经常发生变化,将它们放在PCH中可能会更加减慢编译速度。 - R. Martinho Fernandes
@R.MartinhoFernandes 我认为这取决于情况。我见过预编译头文件(一堆常用的)和并行编译都可以得到非常好的结果。由于速度是问题所在,因此当使用GNU工具时,以下是我的建议。 - epatel
问题在于PCH用于不经常更改的内容。这些玩意儿重新编译需要很长时间。这是我的经验。 - R. Martinho Fernandes
@R.MartinhoFernandes 重新编译只需要一次...如果它被多次包含,你就会得到回报。大项目,大收益;小项目,小收益...(而且也不应该有太多的内容,我们运行统计并选择文件,只包含>2次的文件) - epatel
嗯,你说得对。我想在内联函数的用户数量和更改频率之间存在一定的比例阈值,PCH会很划算。 - R. Martinho Fernandes

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