如果头文件包含函数定义,则编译器可以内联它。如果导出该函数,则在链接期间还必须使客户端可以使用该函数的名称和实现。编译器如何实现这一目标?它是否内联函数并为外部调用者提供实现?
考虑Foo.h:
class Foo
{
int bar() { return 1; }
};
Foo :: bar可以在库foo.so中内联或不内联。如果另一段代码包含Foo.h它是否总是创建自己的Foo :: bar副本,无论是否内联?
答案 0 :(得分:2)
标题文件只是复制粘贴到源文件中 - 这都是#include
所做的。如果使用该关键字声明或在类定义中定义函数,则函数仅为inline
,并且inline
仅为提示;它不会强制编译器生成不同的代码或禁止您执行任何其他操作。
您仍然可以使用inline
函数的地址,或者等效地提及,导出它。对于这些用途,编译器只是将其视为非inline
并使用One Definition Rule(表示用户不能将两个定义应用于同一函数,类等的规则)来“确保” “该函数定义一次,只导出一个副本。通常,您只能在所有来源中使用一个定义;内联函数必须有一个定义,该定义在每个使用的源中都会重复。
以下是关于inline extern
函数(7.1.2 / 4)的标准:
应在中定义内联函数 每个翻译单元 使用过,并且应该完全相同 每种情况下的定义(3.2)。 [注意: 对内联函数的调用可能是 在定义之前遇到过 出现在翻译单元中。 ]如果 具有外部链接的功能是 在一个翻译中声明内联 单位,应在内联合宣布 所有翻译单位 出现;无需诊断。一个 具有外部链接的内联函数 总共应具有相同的地址 翻译单位。一个静态的本地 外部内联函数中的变量 总是指同一个对象。一个 外部内联中的字符串文字 函数是同一个对象 不同的翻译单位。
答案 1 :(得分:1)
这通常意味着它最终会为链接时使用它的每个obj文件创建一个单独的内联方法。它也可能失败或拒绝内联很多东西,因此这可能会导致问题,因为你可以结束膨胀的obj而不会获得内联的性能。虚拟方法内联可能会发生同样的情况,因此值得强制内联和设置内联失败的警告(关于编译器提供的唯一有用的警告消息)。
答案 2 :(得分:0)
通过导出,我猜你的意思是获得指向函数的指针,然后通过指针调用函数。
是的,在这种情况下,编译器将生成一个常规函数,以便可以从指针调用它。
执行此操作的一种方法是使用链接一次部分。这个想法是在翻译单元中获取特殊类型的部分中的代码,该部分具有基于函数名称的名称。在链接期间,链接器将只保留一个具有相同名称的一次性链接的实例。
答案 3 :(得分:-1)
在编译的二进制文件中不存在内联函数:这是因为它们被直接放在调用站点(所谓的IN-LINE)。内联函数的每次使用都会导致在该位置引入完整的代码。
因此无法导出内联函数,因为它们不存在。但是如果你在一个标题中有一个定义,你仍然可以使用它们。是的,你必须提供内联函数的定义,否则你不能使用它。
如果您设法导出内联函数,那么它确定它不再内联:inline不是严格的语义元素。根据编译器和编译器设置,一个编译器可能选择内联,另一个不是,有时提供警告,有时甚至是错误(我更喜欢这是默认行为,因为它显示了意外事件发生的地方)< / p>