为什么在内联函数内部调用的函数不需要定义?
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_free
或 f_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 缺少符号 Y
是 inline
或 static
函数,因为有如果它具有本地链接并且可以忽略不计,如果它是内联的,则不可能从外部模块调用它们。
但是瞧!忽略您能够编译和 运行 这个程序的事实,根据标准,您的程序是 ill-formed。只有在内联函数 X()
的情况下,链接器才会在未在任何编译单元中找到对内联 X()
的引用时立即优化它。
如果不是内联函数,它会停留到链接的最后阶段,并通过模块查找缺少的名称。你的编译器的实现是这样的,它会在排除未使用的 X()
之前被搜索。其他一些编译器的行为可能不同。
这就是为什么它是“ill-formed”代码,但“不需要诊断”。某些实现可以进行诊断。有些实现会将这两种情况都诊断为错误,有些则无法检测到您的欺骗行为。有些实现只有在你有多个编译单元时才会检测到问题。
考虑以下示例:
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_free
或 f_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 缺少符号 Y
是 inline
或 static
函数,因为有如果它具有本地链接并且可以忽略不计,如果它是内联的,则不可能从外部模块调用它们。
但是瞧!忽略您能够编译和 运行 这个程序的事实,根据标准,您的程序是 ill-formed。只有在内联函数 X()
的情况下,链接器才会在未在任何编译单元中找到对内联 X()
的引用时立即优化它。
如果不是内联函数,它会停留到链接的最后阶段,并通过模块查找缺少的名称。你的编译器的实现是这样的,它会在排除未使用的 X()
之前被搜索。其他一些编译器的行为可能不同。
这就是为什么它是“ill-formed”代码,但“不需要诊断”。某些实现可以进行诊断。有些实现会将这两种情况都诊断为错误,有些则无法检测到您的欺骗行为。有些实现只有在你有多个编译单元时才会检测到问题。