我们必须在头文件中定义内联函数的原因是,调用该函数的每个编译单元必须具有整个定义才能替换调用或替换它。我的问题是,如果编译器可以并且确实进行内联的优化,我们不得不在头文件中放置一个定义,这将要求它深入研究无论如何定义函数的cpp文件。
换句话说,编译器在我看来能够看到函数"声明"在头文件中,转到相应的cpp文件并从中提取定义并将其粘贴到另一个cpp中的适当位置。鉴于这种情况,为什么坚持在头文件中定义函数,这意味着编译器就好像不能看到""到其他cpp文件。
MSDN说的是关于Ob2 /优化设置:
Ob2 /默认值。允许扩展标记为内联,__ inline或__forceinline,以及编译器选择的任何其他函数的函数(我的重点)。
答案 0 :(得分:4)
我们被迫在头文件中提供内联函数定义的原因(或者至少是在给定编译单元中内联函数时以实现可见的某种形式)是C ++标准的要求。 / p>
但是,该标准并不会阻止实现(例如工具链或其中的部分工具链,例如预处理器,编译器本身,链接器等)使事情变得更聪明。
某些特定的实现更聪明一些,因此即使在编译器不可见的情况下,实际上也可以内联函数。例如,在基本的“编译所有源文件然后链接”工具链中,智能链接器可能会意识到函数很小并且只调用了几次,并选择(实际上)内联它,即使内联点也是如此编译器不可见(例如,因为调用函数的语句位于不同的编译单元中,函数本身位于另一个编译单元中),因此编译器不会进行内联。
问题是,标准并不妨碍实现这样做。它只是说明了所有实现行为的最低要求集。
基本上,编译器具有内联函数可见性的要求是标准的最低要求。如果以这种方式编写程序(例如,所有要内联的函数都在其头文件中定义),那么标准保证它将适用于每个(标准兼容的)实现。
但这对我们更智能的工具链意味着什么呢?更智能的工具链必须从格式良好的程序中产生正确的结果 - 包括在每个使用这些函数的编译单元中定义内联函数的结果。我们的工具链被允许更聪明地做事(例如在编译单元之间偷看),但是,如果代码以需要这种更聪明的行为的方式编写(例如编译器单元之间的编译器窥视),那么代码可能会被另一个工具链拒绝。
最后,每个C ++实现(工具链,标准库等)都需要符合C ++标准的要求。相反的情况并非如此 - 一个实现可能比标准要求更聪明,但这并不会产生一些要求,即其他一些实现以兼容的方式执行。
从技术上讲,内联不仅限于编译器的功能。它可能发生在编译器或链接器中。它也可能在运行时发生 - 例如“Just In Time”技术实际上可以在运行几次后重构可执行代码以增强后续性能[这通常发生在虚拟机环境中,允许这些技术的好处,同时避免与自修改可执行文件相关的问题]。
答案 1 :(得分:3)
不,编译器传统上不能这样做。在经典模型中,编译器会看到'一次只能有一个cpp文件,并且不能转到任何其他cpp文件。在这个cpp文件编译器中所谓的目标文件采用platofirm原生格式,而不是使用20世纪70年代的有效链接器链接,这就像锤子一样愚蠢。
这种模式正在慢慢发展。随着越来越多的有效链接时间优化(LTO),链接器开始意识到cpp代码是什么,并且可以执行自己的内联。但是,即使使用链接时优化模型,编译器完成内联和优化仍然比链接时更有效 - 当cpp代码转换为适合链接的中间格式时,很多重要的上下文都会丢失。
答案 2 :(得分:2)
inline关键字不仅仅是在调用它时扩展实现,而且实际上主要是关于声明函数的多个定义可能存在于给定的翻译单元中。
以前在其他问题中已经介绍过,这可以解释得比我好得多:)。
Why are class member functions inlined?
Is "inline" implicit in C++ member functions defined in class definition
答案 3 :(得分:1)
如果已经看到该函数的定义,编译器可以更容易地扩展内联函数。让编译器在使用该函数的每个翻译单元中看到函数定义的最简单方法是将定义放在标题中,并在该函数将被使用的任何地方放置#include
标题。当你这样做时,你必须将定义标记为inline
,以便编译器(实际上是链接器)不会抱怨在多个翻译单元中看到该函数的定义。