C++ 内联函数具有非内联副本
在Agner Fog的书中,他写了一个章节“内联函数有一个非内联副本” 函数内联的复杂性在于,同一个函数可能会从另一个模块调用。编译器必须创建内联函数的非内联副本,以防该函数也被另一个模块调用。如果没有其他模块调用该函数,则此非内联副本是死代码。这种代码碎片化会降低缓存的效率 让我们做一个测试 foo.hC++ 内联函数具有非内联副本,c++,gcc,inline,C++,Gcc,Inline,在Agner Fog的书中,他写了一个章节“内联函数有一个非内联副本” 函数内联的复杂性在于,同一个函数可能会从另一个模块调用。编译器必须创建内联函数的非内联副本,以防该函数也被另一个模块调用。如果没有其他模块调用该函数,则此非内联副本是死代码。这种代码碎片化会降低缓存的效率 让我们做一个测试 foo.h inline double foo(double x) { return x; } t1.cpp #include "foo.h" double t1(double x) {
inline double foo(double x) {
return x;
}
t1.cpp
#include "foo.h"
double t1(double x) {
return foo(x);
}
main.cpp
#include <stdio.h>
extern double foo(double);
int main(void) {
printf("%f\n", foo(3.14159));
}
及
//main.cpp
#包括
外部浮动条(int x);
内部主(空){
printf(“%f\n”,条(3));
}
使用
gcc-O3 foo.cpp main编译。cpp
显示foo
在bar
中是内联的,但从未使用过的foo
的非内联副本是二进制的。标准规定内联
方法的完整定义需要在使用它的每个翻译单元中都可见:
应在使用odr的每个翻译单元中定义内联函数,且内联函数应具有精确的
每种情况下的定义相同(3.2)。[…]如果具有外部链接的函数
在一个翻译单元中声明内联,则应在其出现的所有翻译单元中声明内联;
无需诊断
(N4140中的7.1.2/4)
这确实使你的问题中的例子不恰当
此规则还包括来自链接库的任何外部模块的每个TU。它们还需要C++代码中的完整定义,例如通过定义头中的函数。因此,如果当前翻译不需要,编译器可以安全地省略任何类型的“非内联副本”
关于确定副本不存在的问题:标准不保证任何优化,因此这取决于编译器。有或没有附加的
static
关键字。我不知道标准是怎么说的,但对于一般情况来说是有意义的。通常内联/constexpr函数是在头文件中定义的,所以声明它但不定义它的情况不会发生。@kamikaze,可以更具体一点吗。你在“它有意义”中指的是什么…我不知道你所说的声明但未定义是什么意思。很抱歉,我的问题太长了。我认为这一点是,您应该能够创建一个指向内联函数的指针(它将指向非内联版本),并且如果多个翻译单元希望有一个指向“内联函数”的指针,那么该非内联版本的单个实例将导致生成的代码更少@Zboson声明的是当您有一个原型(即签名是已知的)时,比如double foo(double)。主体的完整功能已经定义。@kamikaze,谢谢,我现在明白你的意思了。也许我当时问的问题不对。这是否意味着我的代码有问题?如果我没有在main.cpp
中定义内联函数,我不应该从main.cpp
调用foo
。我就是这样读你的答案的。我的代码示例一定是错的。@Zboson是的,它确实是错的。我添加了一个标准引用。Agner写道:“如果没有其他模块调用该函数,那么这个非内联副本就是死代码。”但为什么其他模块会调用该函数,除非它在同一个翻译单元中定义?好的,谢谢,我想我希望你在回答中指出这一点。我想我还是不知道什么时候应该使用静态内联
而不是只使用内联
。如果代码正确,我看不出在实践中会有什么不同。@Zboson内联
函数仍然具有外部链接,除非声明为静态
。在这种情况下,“具有外部链接的内联函数在所有翻译单元中应具有相同的地址。”
//foo.cpp
int foo(int x) {
return x;
}
float bar(int x) {
return 1.0*foo(x);
}
//main.cpp
#include <stdio.h>
extern float bar(int x);
int main(void) {
printf("%f\n", bar(3));
}