我正在编写一个仅包含标头的库,在声明我提供给用户的函数static
或inline
. 在这种情况下,有什么理由让我更喜欢其中一个吗?
5 回答
它们都提供不同的功能。
inline
使用关键字(§ 7.1.3/4)有两个含义:
- 它提示编译器在调用点替换函数体优于通常的函数调用机制。
- 即使省略了内联替换,也会遵循内联的其他规则(尤其是一个定义规则)。
函数上的static
关键字强制inline
函数具有内部链接(内联函数具有外部链接)此类函数的每个实例都被视为单独的函数(每个函数的地址不同)并且这些函数的每个实例都有自己的副本静态局部变量和字符串文字(内联函数只有一个副本)
注意:此答案适用于 C++。对于 C,请参阅caf 的答案。两种语言不同。
static
有两个相关的含义:
对于命名空间范围内
static
的函数,给出函数内部链接,这实际上意味着该名称对链接器不可见。static
也可以以这种方式用于数据,但对于数据,这种用法在 C++03 中已弃用(C++03 附件 D 中的§D.2,规范性)。尽管如此,默认情况下常量具有内部链接(明确表示不是一个好主意,因为不推荐使用数据)。对于类中的函数,
static
删除隐式this
参数,以便在没有类对象的情况下调用函数。
在头文件中,通常不会对函数使用内部链接,因为不希望在包含头文件的每个编译单元中都复制一个函数。
detail
一个常见的约定是,当需要不属于公共模块接口的类或函数并希望减少对普通名称空间的污染(即,减少名称冲突的可能性)时,改为使用名为 的嵌套名称空间。Boost 库使用此约定。与包含保护符号一样,此约定表示当前 C++ 中缺乏模块支持,在这种情况下,基本上只能通过约定来模拟一些关键的语言特性。
这个词inline
也有两个相关的含义:
对于命名空间范围内的函数,它告诉编译器该函数的定义是有意在每个使用它的编译单元中提供的。实际上,这使得链接器忽略函数的多个定义,并且可以在头文件中定义非模板函数。数据没有对应的语言特性,虽然模板可以用来模拟
inline
数据的效果。不幸的是,它还给编译器一个强烈的提示,即对函数的调用最好在机器代码中“内联”扩展。
第一个含义是 的唯一保证含义inline
。
通常,适用inline
于头文件中的每个函数定义。有一个例外,即直接在类定义中定义的函数。这样的函数是自动声明的inline
(即,在不显式添加单词的情况下避免链接器抗议inline
,因此在此上下文中的一个实际用法是应用于inline
类中的函数声明,以告诉读者该函数的定义稍后跟随在头文件中)。
因此,尽管您似乎对static
and的含义有些困惑,inline
但它们不可互换!- 你基本上是对的,static
并且inline
以某种方式连接。将(自由)函数从类中移出到命名空间范围,您将更改static
→ inline
,将函数从命名空间范围移到类中,您将更改inline
→ static
。虽然这不是一件常见的事情,但我发现在重构仅标头代码时它并不少见。
加起来:
用于
inline
标头中定义的每个命名空间范围函数。特别是inline
用于函数模板的特化,因为函数模板只能是完全特化的,而完全特化是一个普通函数。未能inline
在头文件中应用函数模板特化,通常会导致链接错误。使用一些特殊的嵌套命名空间,例如调用
detail
以避免内部实现细节名称的污染。用于
static
静态类成员。不要
static
用来明确表示常量具有内部链接,因为static
在 C++03 中不推荐使用这种使用(即使显然在 C++11 中已删除了弃用)。请记住,虽然
inline
不能应用于数据,但可以通过使用模板来实现几乎相同的(实践中)效果。但是,如果您确实需要在标头中实现的大量共享常量数据,我建议您通过inline
函数生成对数据的引用。编写代码要容易得多,对于代码的读者来说也更容易理解。:-)
static
并且inline
是正交的。换句话说,你可以有一个或两个或一个都没有。每个都有自己独立的用途,决定是否使用它们。
如果该函数不共享类状态:static
. 使函数static
可以从在任何地方调用中受益。
如果功能比较小而且清晰:inline
(此答案基于 C99 规则;您的问题同时标记为 C 和 C++,C++ 中的规则可能会有所不同)
如果将函数声明为 just inline
,则函数的定义是内联定义。编译器不需要对翻译单元中函数的所有调用使用内联定义 - 允许假设在另一个翻译单元中提供了相同函数的外部定义,并将其用于部分或所有调用. 在仅头文件库的情况下,不会有这样的外部定义,因此使用它的程序可能会在链接时因缺少定义而失败。
另一方面,您可以将函数声明为inline
和 static
。在这种情况下,编译器必须使用您为翻译单元中对函数的所有调用提供的定义,无论它是否实际上内联它们。inline static
这适用于仅包含头文件的库(尽管编译器对于声明的函数的行为可能与仅声明的函数的行为完全相同static
,但在这两种情况下都内联了它认为有益的地方,因此在实践中可能几乎没有只是被超越static
)。