我阅读了有关“常规”和内联函数的事实:
在前面的章节中,我们反复说过,你不应该在头文件中定义函数,因为如果你在几个 .cpp 文件中包含一个带有函数定义的头文件,那么函数定义也会被复制多次。然后,当您链接文件时,链接器将抛出一个错误,表明您多次定义相同的函数。
但是,内置函数不受此规则的约束,因为源代码中没有重复 - 函数的定义是一个,并且在通过链接器连接 .cpp 文件时不应有任何冲突。
为什么不能在头中定义函数很清楚:链接包含该头的多个模块时会发生“多重定义”。但是从文本中根本不清楚为什么该规则不适用于内联函数。我是这样理解的:“内联函数不重复,因为它们不重复。” 狂欢。
当然,我可以理所当然地考虑这个事实,仅此而已,但是我仍然想了解为什么会发生这种情况,但是本文没有给出详尽的解释。
如果语言规范说不应该有错误,那么就不应该有错误。然后开始实施细节。你为什么决定它们不重复?
在经典实现中,编译器决定在编译多个翻译单元时为其生成主体的具有外部绑定的内联函数当然是重复的。在编译期间,每个翻译单元都会在其目标文件中获得自己的同名函数副本。
然而,这些函数在目标文件中以一种特殊的方式被标记——这样当在链接过程中发现同一个外部符号的多个副本时,链接器不会给出错误,而是默默地删除所有副本,只留下一个。也就是说,C++ 编译器生成散布在不同目标文件中的同名函数的“转储”,然后链接器将所有内容收集在一起并清理此“转储”。
在 *nix 系列的编译器中,这个标记是导出符号的名称,即所谓的。“弱”(弱)性格。MSVC++ 编译器中有类似的符号
selectany。只有在链接过程中遇到两个或更多相同的“强”字符时,链接器才会抛出多重定义错误。如果只有一个“强”符号(其余为“弱”),则“强”符号获胜,“弱”符号被丢弃。如果根本没有“强”符号,而只有“弱”符号,则“弱”符号中的一个(一些)获胜。没有错误报告。当编译器决定为具有外部链接的内联函数生成主体时,它只是将链接器的相应符号标记为“弱”,仅此而已。
(模板函数的翻译建立在相同的机制上,如您所知,它也在头文件中定义,并且还在所有需要实例化的目标文件中生成它们的副本。)
例如,通过将这样一个简单的源代码编译到目标文件中
并查看此目标文件的内容,
nm我们将看到一个字母
W标记一个“弱”字符,一个字母标记T一个“强”字符。在生成此类函数主体的所有目标文件中inline,它将以相同的名称出现,并_Z3barv带有标记W。请注意,通过生成多个具有不同名称的函数来解决这个问题是不可能的:在所有其他方面,内联外界函数的行为应该与任何其他外界函数一样,例如,它必须具有相同的所有翻译单元中的地址。
这种方法的副作用是形成目标文件的“经典”方法,其中函数没有开始和结束,只有一个入口点,变得不可接受。为了能够从目标文件中排除函数,C++ 编译器被迫在目标文件中以紧凑的形式形成函数体。
历史上有一些尝试采取不同行动的替代实现的例子。“多遍”实现在第一次编译时根本没有为内联和模板函数生成主体。他们进行了预链接,在那里他们收集了有关哪些函数实际需要主体的信息,然后再次调用编译器并为这些函数编译独特的主体,然后进行最终链接。但是在流行的编译器(GCC / Clang / MSVC)中,这种方法并没有扎根。
如果一个内联函数真的被内联了,那么它就好像它不存在一样,所以原则上不能重复。
如果优化后它没有被内联,那么它在每个翻译单元中都有一个唯一的名字,结果,我们有很多函数做同样的事情,但有不同的名字。由于名称最终不同,因此不会发生多重定义。
在此处阅读更多内容:C++11 标准、3.2.5(一个定义规则)、7.1.2(函数说明符)。
我在哪里可以获得 C++ 标准?