Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/152.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 如果编译器可以自动内联函数,为什么必须在头中“定义”内联函数_C++_Function_Optimization_Compilation_Inline - Fatal编程技术网

C++ 如果编译器可以自动内联函数,为什么必须在头中“定义”内联函数

C++ 如果编译器可以自动内联函数,为什么必须在头中“定义”内联函数,c++,function,optimization,compilation,inline,C++,Function,Optimization,Compilation,Inline,我们必须在头中定义内联函数的原因是,调用该函数的每个编译单元必须具有完整的定义,以便替换或替换调用。我的问题是,如果编译器能够并且确实进行了自己的内联优化,那么为什么我们必须在头文件中添加一个定义,这将要求它挖掘定义函数的cpp文件 换句话说,在我看来,编译器似乎能够在头文件中查看函数声明,转到相应的cpp文件,从中提取定义并将其粘贴到另一个cpp中的适当位置。既然如此,为什么还要坚持在头文件中定义函数,这意味着编译器无法查看其他cpp文件 MSDN说明了Ob2/优化设置: Ob2/默认值。允许

我们必须在头中定义内联函数的原因是,调用该函数的每个编译单元必须具有完整的定义,以便替换或替换调用。我的问题是,如果编译器能够并且确实进行了自己的内联优化,那么为什么我们必须在头文件中添加一个定义,这将要求它挖掘定义函数的cpp文件

换句话说,在我看来,编译器似乎能够在头文件中查看函数声明,转到相应的cpp文件,从中提取定义并将其粘贴到另一个cpp中的适当位置。既然如此,为什么还要坚持在头文件中定义函数,这意味着编译器无法查看其他cpp文件

MSDN说明了Ob2/优化设置:

Ob2/默认值。允许扩展标记为inline、_inline或_forceinline的函数,以及编译器选择的任何其他函数


不,编译器传统上不能这样做。在经典模型中,编译器一次只能“看到”一个cpp文件,不能转到任何其他cpp文件。在这个cpp文件编译器中,有一个柏拉图公司的原生格式的所谓对象文件,它是使用1970年代的有效链接器链接的,这个链接器非常愚蠢


这种模式正在缓慢发展。随着越来越有效的链接时间优化,LTO链接器会意识到什么是cpp代码,并可以执行自己的内联。然而,即使使用链接时间优化模型,编译器进行内联和优化仍然比链接时间更有效——当cpp代码转换为适合链接的中间格式时,许多重要的上下文都会丢失。

不,编译器传统上无法做到这一点。在经典模型中,编译器一次只能“看到”一个cpp文件,不能转到任何其他cpp文件。在这个cpp文件编译器中,有一个柏拉图公司的原生格式的所谓对象文件,它是使用1970年代的有效链接器链接的,这个链接器非常愚蠢


这种模式正在缓慢发展。随着越来越有效的链接时间优化,LTO链接器会意识到什么是cpp代码,并可以执行自己的内联。然而,即使链接时间优化模型编译器完成了,内联和优化仍然比链接时间更有效-当cpp代码转换为适合链接的中间格式时,许多重要的上下文丢失。

内联关键字不仅仅是在调用时扩展实现,但实际上主要是关于声明一个函数的多个定义可能存在于一个给定的翻译单元中

这在之前的其他问题中已经讨论过,它能比我更好地解释:


inline关键字不仅仅是在调用时扩展实现,实际上主要是声明一个函数的多个定义可能存在于给定的翻译单元中

这在之前的其他问题中已经讨论过,它能比我更好地解释:


如果编译器已经看到函数的定义,那么它就更容易内联扩展该函数。让编译器在使用某个函数的每个翻译单元中看到该函数定义的最简单方法是将该定义放在一个头中,并将该头包含在将使用该函数的任何位置。当您这样做时,您必须将定义标记为内联,这样编译器实际上链接器就不会抱怨在多个翻译单元中看到该函数的定义。

如果编译器看到该函数的定义,那么它就更容易内联扩展该函数。让编译器在使用某个函数的每个翻译单元中看到该函数定义的最简单方法是将该定义放在一个头中,并将该头包含在将使用该函数的任何位置。当您这样做时,您必须将定义标记为内联,以便编译器实际上链接器不会抱怨在多个翻译单元中看到该函数的定义。

我们被迫在头文件中提供内联函数定义的原因,或者至少,当在给定编译单元中嵌入函数时,实现的某些形式是C++标准的要求。 然而,该标准并没有刻意阻止实现,例如工具链或其部分,如预处理器、编译器本身、链接器等,使事情变得更加智能

有些特定的实现做得更聪明一些,因此即使在编译器看不到函数的情况下,也可以实际内联函数。例如,在基本编译中,编译所有源文件,然后链接工具链 智能链接器可能会意识到一个函数很小,只调用了几次,并选择实际上将其内联,即使内联发生的点对编译器不可见,例如,因为调用函数的语句位于单独的编译单元中,函数本身位于另一个编译单元中,因此编译器不会进行内联

问题是,标准并没有阻止实现这样做。它只是说明了所有实现行为的最低要求集

本质上,编译器对要内联的函数具有可见性的要求是标准的最低要求。如果程序是以这种方式编写的,例如,所有要内联的函数都在其头文件中定义,那么标准保证它将与每个符合标准的实现一起工作

但这对我们更智能的工具链意味着什么?更智能的工具链必须从格式良好的程序中生成正确的结果,包括在使用这些函数的每个编译单元中定义内联函数的程序。我们的工具链允许做一些更智能的事情,例如在编译单元之间窥视,但是,如果代码的编写方式需要更智能的行为,例如编译器在编译单元之间窥视,那么代码可能会被另一个工具链拒绝

<>最后,需要遵循C++标准的要求,实现工具链、标准库等的C++实现。反之亦然——一个实现可能比标准要求做得更聪明,但这并不要求其他实现以兼容的方式做事情


从技术上讲,内联并不局限于编译器的功能。它可能发生在编译器或链接器中。它也可能发生在运行时——例如,实时技术实际上可以在可执行代码运行几次后重新构造可执行代码,以提高后续性能[这通常发生在虚拟机环境中,它允许这些技术的好处,同时避免与自修改可执行文件相关的问题]

< P>我们被迫在头文件中提供内联函数的定义,或者至少在某种形式下,当在给定编译单元中嵌入函数时,实现的形式是C++标准的要求。 然而,该标准并没有刻意阻止实现,例如工具链或其部分,如预处理器、编译器本身、链接器等,使事情变得更加智能

某些特定的实现会做得更聪明一些,因此即使在编译器看不到函数的情况下,也可以实际内联函数。例如,在基本编译所有源文件然后链接工具链时,智能链接器可能会意识到函数很小,只调用了几次,并选择内联函数即使编译器看不到内联发生的点(例如,因为调用函数的语句位于单独的编译单元中),函数本身也位于另一个编译单元中,因此编译器不会进行内联

问题是,该标准并没有阻止一个实现这样做,它只是说明了所有实现行为的最低要求

本质上,编译器对要内联的函数具有可见性的要求是标准的最低要求。如果程序是以这种方式编写的,例如,所有要内联的函数都是在其头文件中定义的,那么标准保证它将与每个符合标准的实现一起工作

但这对我们更智能的工具链意味着什么?更智能的工具链必须从格式良好的程序中产生正确的结果,包括在使用这些函数的每个编译单元中定义内联函数的程序。我们的工具链可以做更智能的事情,例如在编译单元之间窥视,但是如果代码它的编写方式需要更智能的行为,例如,编译器在编译单元之间窥视代码可能被另一个工具链拒绝

最后,每个C++实现工具链、标准库等都是符合C++标准的要求的。相反的是不正确的——一个实现可能会比标准要求更聪明,但是这并不要求其他的实现以兼容的方式来做事情。 从技术上讲,内联并不局限于编译器的功能。它可能发生在编译器或链接器中。它也可能发生在运行时-例如,实时技术实际上可以在运行几次后重新构造可执行代码,以提高后续性能[这通常发生在


n一个虚拟机环境,它允许利用这些技术的优点,同时避免与自修改可执行文件相关的问题]。

没有相应的cpp文件-函数定义可以在任何源文件中,也可以在以后链接到程序的任何目标文件或库中。换句话说,这个案例根本没有给出。@molbdnilo我明白了,对不起,我说相应的cpp文件是错误的。我仍然想知道编译器如何在不使用inline关键字的情况下,按照自己认为合适的方式内联函数。例如,MSDN说:/Ob2。默认值。允许扩展标记为inline、_inline或_forceinline的函数以及编译器选择的任何其他函数。我猜这是在编译阶段,而不是在链接器阶段。没有相应的cpp文件-函数定义可以在任何源文件中,也可以在以后链接到程序的任何目标文件或库中。换句话说,这个案例根本没有给出。@molbdnilo我明白了,对不起,我说相应的cpp文件是错误的。我仍然想知道编译器如何在不使用inline关键字的情况下,按照自己认为合适的方式内联函数。例如,MSDN说:/Ob2。默认值。允许扩展标记为inline、_inline或_forceinline的函数以及编译器选择的任何其他函数。我猜这是在编译阶段,不是在链接器阶段。我明白了。Visual studio中发布模式的默认优化记录为:/Ob2。默认值。允许扩展标记为inline、_inline或_forceinline的函数以及编译器选择的任何其他函数。所以它不做自己的内联?@TitoneMaurice,对不起,我不熟悉MSVC工具链。我明白了。Visual studio中发布模式的默认优化记录为:/Ob2。默认值。允许扩展标记为inline、_inline或_forceinline的函数以及编译器选择的任何其他函数。所以它不做自己的内联?@TitoneMaurice,我不熟悉MSVC工具链,抱歉。谢谢。引用您给出的第二个链接的答案:inline关键字与内联几乎没有任何关系。这是关于函数的多个相同定义是否允许在不同的tu中使用。引用您给出的第二个链接的答案:inline关键字与内联几乎没有任何关系。这是关于在不同的TU中是否允许使用多个相同的函数定义。回答很好,很有教育意义。我理解的是,即使没有满足C++语言标准,编译器和链接器也可能会采取额外的步骤,在这种情况下,在头中提供完整的定义。因此,可以使用您的语言,通过编译器或链接器查看编译单元来完成内联优化。不过这很有趣,因为最佳实践是在标题中声明,在CPP中定义,但内联与此相反,我已经习惯了这种做法。谢谢。链接时内联需要编译器的全部智能。这不是一个普通的链接器所能做到的。链接时间优化是存在的,但它的工作原理是将编译器的中间表示形式存储到对象文件中,而不是读取最终的asm和基于此的内联。至少gcc和clang是这样,我假设其他编译器也可以做LTO。回答很好,很有教育意义。我理解的是,即使没有满足C++语言标准,编译器和链接器也可能会采取额外的步骤,在这种情况下,在头中提供完整的定义。因此,可以使用您的语言,通过编译器或链接器查看编译单元来完成内联优化。不过这很有趣,因为最佳实践是在标题中声明,在CPP中定义,但内联与此相反,我已经习惯了这种做法。谢谢。链接时内联需要编译器的全部智能。这不是一个普通的链接器所能做到的。链接时间优化是存在的,但它的工作原理是将编译器的中间表示形式存储到对象文件中,而不是读取最终的asm和基于此的内联。至少gcc和clang是这样,我假设其他编译器也可以执行LTO。