为什么在内联函数内部调用的函数不需要定义?

Why does a function called inside a inline function not require definition?

考虑以下示例:

extern void not_defined();

void f() {
    not_defined();
}

int main() {}

如果我要编译并 link 上面的程序,我会得到一个 linker 错误 undefined reference to not_defined()。 (https://godbolt.org/z/jPzscK7ja)

这是预料之中的,因为我是 ODR 使用 not_defined

但是,如果我将 f() 设为内联函数,程序将编译并 links 正确 (https://godbolt.org/z/qEEob9ann):

extern void not_defined();

// inline is added here
inline void f() {
    not_defined();
}

int main() {}

这也适用于成员函数的内联定义(因为它们也是内联的?)(https://godbolt.org/z/xce3neofW):

extern void not_defined();

struct C {
    void f_member() { 
        not_defined(); 
    }
    
    void recurse() { 
        f_member(); 
    };
};

inline void f_free_inline() {
    not_defined();
}

// void f_free() {
//     not_defined();
// }

int main() {
    C c;
    // f_free_inline();
}

如果我在 main() 中取消对 f_freef_free_inline() 的注释,它将不再有效。这种效果似乎甚至可以传递,因为 recurse 调用 f_member,而 f_member 调用 not_defined().

所以我的问题是,使这成为可能的内联函数有什么特别之处?

编辑:

如果内联函数在模块的范围内(即使它未导出),此技巧将停止工作:

export module mod_interface;

extern void not_defined();

export inline void f_module() {
  not_defined();
}

以上将导致 not_defined 的 linker 错误。

这很可悲,因为我正在将一个库移植到模块中,突然得到了很多未定义的引用。其中的一些函数被包装在内联函数中。

标准不仅仅是对编译器行为的一套要求。也是对程序员行为的一套要求。

GCC 的链接器不会抱怨 X() ODR-used 缺少符号 Yinlinestatic 函数,因为有如果它具有本地链接并且可以忽略不计,如果它是内联的,则不可能从外部模块调用它们。

但是瞧!忽略您能够编译和 运行 这个程序的事实,根据标准,您的程序是 ill-formed。只有在内联函数 X() 的情况下,链接器才会在未在任何编译单元中找到对内联 X() 的引用时立即优化它。

如果不是内联函数,它会停留到链接的最后阶段,并通过模块查找缺少的名称。你的编译器的实现是这样的,它会在排除未使用的 X() 之前被搜索。其他一些编译器的行为可能不同。

这就是为什么它是“ill-formed”代码,但“不需要诊断”。某些实现可以进行诊断。有些实现会将这两种情况都诊断为错误,有些则无法检测到您的欺骗行为。有些实现只有在你有多个编译单元时才会检测到问题。