我知道声明为
inline
的函数必须在其自己的翻译单元中有定义(正如标准所说:内联函数应在使用它的每个翻译单元中定义)。
之前,我对这个要求的理解是,编译器会尝试内联使用
inline
关键字声明的函数,因此虽然内联不是强制性的,但定义首先应该对编译器可见。
根据这种理解,我认为不提供定义应该会导致编译器错误。事实上,我从来没有编写过没有定义的
inline
函数。但后来我在我的玩具程序中故意尝试了这个错误,它导致了链接器错误。
所以,我只是好奇为什么 C++ 不将这个问题保留为编译时错误并让编译器捕获该错误?根据我的理解,由于
inline
函数默认具有外部链接,因此链接器到底如何在幕后捕获此错误?
一个例子:
// main.cpp:
inline void InlineFun();
int main()
{
InlineFun();
}
// other.cpp:
#include<iostream>
#include<string>
inline void InlineFun()
{
std::cout << "From other TU" << std::endl;
}
我在 Windows 10 上使用 Visual Studio 2022 和 C++20。我遇到的错误是
LNK2001
和LNK1120
。评论中提供的示例也非常好。
inline
的主要作用是允许同一函数有多个定义(因为定义通常位于多个翻译单元中包含的标头中)。
“inline”这个名字是转移注意力的;与
register
提示一样,除了“接受多个定义”效果之外,它对于现代编译器来说已经过时了。
现代编译器选择内联或不完全独立于
inline
关键字的存在。
该标准要求内联函数的定义应可从使用它的翻译单元“访问”(显然,调用就是这样的使用)。这里的情况并非如此,因为该行为是不是未定义的,需要实现来发出诊断消息,VS 和 gcc 就是这样做的。
考虑到
inline
的非强制性质,构建系统尝试将缺少定义的函数视为非内联函数(在发出强制诊断消息之后)并尝试在链接时解析符号是有意义的。
不会发生链接器错误,因为调用点缺少函数定义;正如您所观察到的,这是编译器的域。这是两个文件的简短记录,一个定义了一个声明为内联的函数,一个定义了一个普通函数。我编译两者并使用
nm
检查目标文件。 nm
检查目标文件;我们用它来显示“全局符号”(导出的或未解析的)。 “T”表示该函数是在“文本”(即代码部分)中定义的,正如人们所期望的那样。
$ gcc -c -O0 *.c
$ cat inlinefunc.c
inline void myfunction(){}
$ cat not-inlinefunc.c
void myfunction(){}
$ gcc -c -O0 *.c
$ nm -g inlinefunc.o
$ nm -g not-inlinefunc.o
0000000000000000 T myfunction
声明为内联的函数作为全局符号不可见,并且不可用于链接。事实上,检查源代码可以确认没有遗漏任何代码。
确实发生了链接器错误因为该函数也在定义站点被声明为内联。该符号根本没有导出。要成功链接,请删除定义函数的位置的
inline
。 (我们可以得出结论,此处检查的 C++ 编译器不会以任何特殊方式“修饰”内联函数的名称。)