C++ 内联函数具有非内联副本

C++ 内联函数具有非内联副本,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) {

在Agner Fog的书中,他写了一个章节“内联函数有一个非内联副本”

函数内联的复杂性在于,同一个函数可能会从另一个模块调用。编译器必须创建内联函数的非内联副本,以防该函数也被另一个模块调用。如果没有其他模块调用该函数,则此非内联副本是死代码。这种代码碎片化会降低缓存的效率

让我们做一个测试

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));
}

//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));
}