在Agner Fog的Optimizing C++ manual中他有一个部分"内联函数有一个非内联副本"他写的地方
函数内联的复杂性在于可以从另一个模块调用相同的函数。编译器必须制作内联函数的非内联副本,以便可以从另一个模块调用该函数。如果没有其他模块调用该函数,则此非内联副本为死代码。这种代码碎片使得缓存效率降低。
让我们对此进行测试。
foo.h中
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));
}
使用g++ t1.cpp main.cpp
进行编译,并且运行正常。如果我g++ -S t1.cpp main.cpp
并查看程序集,我会看到main.s
调用t1.s
中定义的函数。执行g++ -c main.cpp
和g++ t1.cpp
并查看带有nm
的符号会在U _Z3food
中显示main.o
,在W _Z3food
中显示t1.o
。所以很明显,Agner声称存在非内联副本是正确的。
g++ -O1 t1.cpp main.cpp
怎么样? 由于foo
未定义而无法编译。执行g++ -O1 t1.cpp
和nm t1.o
表示_Z3food
已被删除。
现在我很困惑。我没想到g ++会在启用优化的情况下删除非内联副本。
似乎启用优化inline
相当于static inline
。但是没有优化inline
意味着生成了非内联副本。
也许GCC认为我不会想要非内联副本。但我能想到一个案例。让我们说我想创建一个库,在库中我想要一个在多个转换单元中定义的函数(这样编译器可以在每个转换单元中内联函数的代码)但我还想要一个外部模块链接到我的库,可以调用库中定义的函数。我显然需要一个非内联版本的函数。
如果我不希望非内联副本使用static inline
,Agner会给出一个建议。但是从这个question and answers我推断这只对显示意图有用。因此,一方面它清楚它不仅仅是意图而不使用优化,因为它是非内联副本。但另一方面,通过优化,它实际上似乎只显示了意图,因为非内联副本被剥离了。这很令人困惑。
我的问题:
static inline
,应该总是有非内联副本吗?static inline
吗?我刚才意识到我可能误解了Agner的陈述。当他说函数inlinng时,他可能会向编译器推荐倾斜代码,而不是使用inline
关键字。换句话说,他可能指的是用extern
而不是inline
或static
定义的函数。
例如
//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));
}
使用gcc -O3 foo.cpp main.cpp
进行编译显示foo
内联bar
,但是从未使用的foo
的非内联副本位于二进制文件中。
答案 0 :(得分:6)
标准规定,inline
方法的完整定义需要在每个使用它的翻译单元中都可见:
内联函数应在每个使用过的翻译单元中定义,并且应具有确切的内容 在每种情况下(3.2)都有相同的定义。 [...]如果具有外部链接的功能是 在一个翻译单元中内联声明,应在其出现的所有翻译单元中内联声明; 无需诊断。
(N4140中的7.1.2 / 4)
这确实使你的问题中的例子格式不正确。
此规则还包括链接库的外部模块中的每个TU。他们还需要C ++代码中的完整定义,例如:通过在标题中定义函数。因此,如果当前的翻译不需要,编译器可以安全地省略任何类型的“非内联副本”。
确定副本不存在:标准不保证任何优化,因此由编译器决定。有和没有额外的static
关键字。