我应该使用静态还是内联?

时间:2012-11-20 03:19:14

标签: c++ c static inline

我正在编写一个仅限标题的库,在宣布我向用户staticinline提供的功能之间,我无法下定决心。在这种情况下,我有理由为什么要选择其中一个?

5 个答案:

答案 0 :(得分:6)

它们都提供不同的功能。

使用inline关键字(第7.1.3 / 4节)有两个含义:

  1. 提示编译器,在通常的函数调用机制中,函数体的替换优于通常的函数调用机制。
  2. 即使省略内联替换,也会遵循内联的其他规则(尤其是w.r.t 一个定义规则)。
  3. 函数上的static关键字强制inline函数具有内部链接(内联函数具有外部链接)此类函数的每个实例都被视为单独的函数(每个函数的地址不同),这些函数的每个实例都有自己的静态局部变量和副本的副本。字符串文字(内联函数只有这些的一个副本)

答案 1 :(得分:3)

staticinline是正交的。换句话说,您可以拥有其中之一或两者或没有。每个都有自己独立的用途,决定是否使用它们。

答案 2 :(得分:3)

注意:这个答案适用于C ++。对于C,请参阅caf's answer。这两种语言不同。

static有两个相关的含义:

  • 对于命名空间范围内的函数,static提供函数内部链接,实际上这意味着该名称对链接器不可见。 static也可以这种方式用于数据,但是对于数据,这种用法在C ++ 03中被弃用(C ++ 03附录D中的§D.2,规范性的)。尽管如此,常量默认情况下还有内部链接(因为不推荐使用数据,所以明确这一点并不是一个好主意。)

  • 对于类中的函数,static删除隐式this参数,以便在没有类的对象的情况下调用函数 。 / p>

在标题中,通常不使用函数的内部链接,因为不希望在包含标题的每个编译单元中复制函数。

一个常见的约定是使用名为 detail 的嵌套命名空间,当需要不属于公共模块接口的类或函数时,并希望减少污染普通命名空间(即减少名称冲突的可能性)。此约定由Boost库使用。与包含保护符号的方式相同,此约定表示当前C ++中缺少模块支持,其中一个基本上被简化为通过约定模拟一些关键语言功能。

单词inline也有两个相关的含义:

  • 对于命名空间范围内的函数,它告诉编译器函数的定义是在每个使用它的编译单元中有意提供的。实际上,这使得链接器忽略函数的多个定义,并且可以在头文件中定义非模板函数。虽然模板可用于模拟数据的inline效果,但数据没有相应的语言功能。

  • 遗憾的是,它还为编译器提供了一个强大的提示,调用该函数最好在机器代码中“内联”扩展。

第一个含义是inline的唯一保证含义。

通常,将inline应用于头文件中的每个函数定义。有一个例外,即直接在类定义中定义的函数。这样的函数自动声明为inline(即,在没有明确添加单词inline的情况下避免链接器抗议,因此在此上下文中的一个实际用法是将inline应用于函数声明一个类,告诉读者该函数的定义稍后会出现在头文件中。)

所以,虽然看起来你对staticinline的含义感到有点困惑 - 但它们并不是可以互换的! - 你基本上对staticinline以某种方式联系在一起。将(免费)函数从类移到命名空间范围,您可以更改staticinline,并将函数从命名空间范围移动到类中,您可以更改inline→{ {1}}。虽然这不常见,但我发现在重构只有标题的代码时并不罕见。

总结:

  • 对标头中定义的每个命名空间范围函数使用static。特别是,使用inline来实现函数模板的特化,因为函数模板只能是完全专用的,而完全特化是一个普通的函数。如果未将inline应用于头文件中的函数模板特化,通常会导致链接错误。

  • 使用一些特殊的嵌套命名空间,例如称为inline以避免内部实施细节名称的污染。

  • 对静态类成员使用detail

  • 不要使用static明确表示常量具有内部链接,因为在C ++ 03中不推荐使用static(尽管显然已删除了C ++ 11)。

  • 请记住,虽然static无法应用于数据,但可以通过使用模板实现几乎相同(实际)效果。但是,如果您确实需要在标头中实现的大量共享常量数据,我建议通过inline函数生成对数据的引用。编码起来要容易得多,而且对于代码的读者来说更容易理解。 : - )

答案 3 :(得分:1)

如果该函数没有共享类状态:static。使函数static可以从任何地方被调用中受益。

如果功能相对较小且清晰:inline

答案 4 :(得分:1)

(这个答案基于C99规则;你的问题用C和C ++标记,C ++中的规则可能不同)

如果您将该函数声明为inline,则函数的定义是内联定义。编译器不需要对转换单元中对函数的所有调用使用内联定义 - 允许假设在另一个转换单元中提供了相同函数的外部定义,并将其用于某些或所有调用。对于仅限标头的库,将是这样的外部定义,因此使用它的程序可能会在链接时失败并且缺少定义。

另一方面,您可以将该功能声明为inline static。在这种情况下,编译器必须使用您为翻译单元中的函数调用提供的定义,无论它是否实际内联它们。这适用于仅限标头的库(尽管编译器对于声明为inline static的函数的行为可能与仅仅声明static的函数完全相同,但在这两种情况下,它都会在内联的情况下进行内联。这是有益的,所以在实践中可能只有static只能获得。