C99inline
语义很微妙——事实上,语言的整个部分(存储持续时间与链接、暂定和内联定义)都是一团糟。
虽然在包含存储类说明符(或)inline
的定义中充当编译器提示并且基本上可以忽略,但如果没有说明符,语义会发生变化。static
extern
像这样的定义有inline int fun(void) { ... }
两件事:
首先,它声明了一个带有外部链接的标识符,而不提供相应的外部定义。这意味着这样的定义必须由不同的翻译单元提供,否则我们最终会出现未定义的行为(可能表现为链接失败)。
其次,它提供了一个内联定义,它是外部定义的替代方案。由于函数体在当前翻译单元中是可见的,编译器可以使用它来内联函数或进行类型特化。
为了获得外部定义,直到最近,我认为有必要在另一个翻译单元中重复函数定义(或者用 4 行预处理器代码伪造它)。
但是,这不是必需的:一行代码——包含说明符的函数的重新声明extern
——足以使内联定义成为外部定义。
在您的示例中,这意味着将
inline int foo(void)
{
return 42;
}
进入头文件foo.h
并提供foo.c
包含内容的源文件
#include "foo.h"
// force definition to be external instead of inline
// I believe inline could be omitted, but it doesn't hurt
extern inline foo(void);
为什么这很有用?由于 C 缺少模板,泛型代码通常会带来性能损失,因为您需要使用void*
函数指针(或者,在更复杂的情况下,vtables)来伪造泛型。
然而,一个足够聪明的优化器可以恢复模板的大部分(可能是所有)性能优势,但(在没有链接时优化的情况下)只有在当前翻译单元中相关函数定义可见时。
虽然这可以通过将static
定义放在头文件中来实现,但这可能会以与 C++ 模板相同的方式将代码大小增加到不可接受的水平。
相比之下,使用 C99inline
函数,编译器可以自由地忽略内联定义而支持外部定义,它甚至可以驻留在共享库中。
从中受益的函数的一个很好的例子是qsort()
,在 .in 中有一个内联定义,stdlib.h
在libc.so
. 没有先验理由qsort()
比std::sort()
.