我在头文件中定义了一个类,并在同一个头文件中实现了它的功能。我没有将inline关键字与函数定义放在一起,因为我认为编译器默认将它视为内联函数 - 但是内联只是编译器的一个提示,对吧?如果编译器因其长度而不将其视为内联函数会怎么样?我从来没有收到错误消息'多个定义'实际上。
struct tmp {
void print() {
...(very long)
}
};
答案 0 :(得分:2)
我没有将inline关键字与函数定义放在一起,因为我认为编译器默认将它视为内联函数
是的,在类的主体中定义的成员函数是隐式的inline
。该关键字不是必需的。
inline只是编译器的一个提示,对吧?如果编译器由于其长度而不将其视为内联函数会怎么样?
是的,有点儿。实际上,inline
关键字有两个含义。
第一个是您正在考虑的那个,它提示优化器在调用站点的函数体中内联代码。正如你所说,这只是一个提示 - 优化器可以自由地忽略这个请求,如果它确定这将是一个性能上的悲观化(或者如果它无法内联其他技术原因)。内联关键字的这个含义可以说是过时的。现在所有优化编译器都忽略了inline关键字,因为他们的作者认为他们的启发式算法比程序员更聪明。这几乎总是如此,通过内联标记函数来尝试再次猜测优化器是没有意义的。
inline关键字的第二个含义是放宽单定义规则(ODR),使得链接器可见的同一函数的多个定义合法。 (虽然在这种情况下链接器的行为是一个实现细节,但大多数都只是随意选择其中一个定义。当然,只有它们都是相同的才能正常工作。)inline关键字的这个含义仍然是非常重要,并解释了为什么今天仍然在代码中使用它。
这就是您的代码受益的含义。由于在类的主体中定义的成员函数是内联隐式标记的,因此不会从链接器中获得多重定义的符号错误。
如果你在头文件中定义了这个函数,但是在类定义中不是 - 换句话说,如果你这样做了:
struct tmp {
void print();
};
void tmp::print()
{ ... }
然后,只要该头文件包含在两个或更多个compilands(即,翻译单元)中,您就会开始获得多重定义的符号错误。这是您需要在函数定义中添加inline
关键字的地方,而不是因为您希望编译器能够"内联"它,但因为你想要免除ODR。
答案 1 :(得分:0)
编辑 @Leon(下方)表示我的回答(转载如下)是不正确的。正确的答案被描述为here - 简而言之,如果编译器决定不使函数内联,它仍然将它放在目标模块中。但是链接器将在不同模块中选择一个(可能很多)副本并丢弃所有其他模块。
你是对的:你不会得到“多重定义”错误,因为每次编译器决定不将函数内联时,它会使函数static
在当前模块。这意味着您可以通过代码填充大量功能的大量副本。