【问题标题】:Should one never use static inline function?永远不要使用静态内联函数吗?
【发布时间】:2012-06-04 06:02:53
【问题描述】:

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

  1. 提示编译器在调用点替换函数体优于通常的函数调用机制。
  2. 即使省略了内联替换,也会遵循内联的其他规则(尤其是 w.r.t 一个定义规则)。

如果需要,通常任何主流编译器都会在调用点替换函数体,因此实际上不需要将函数 inline 标记为 #1

进一步 w.r.t #2,据我了解,当您将函数声明为 static inline 函数时,

函数上的static 关键字强制inline 函数具有内部链接(内联函数具有外部链接)此类函数的每个实例都被视为单独的函数(每个函数的地址不同)并且这些函数的每个实例都有自己的静态局部变量和字符串文本的副本(内联函数只有一个副本

因此,这样的函数就像任何其他 static 函数一样,关键字 inline 不再重要,它变得多余。

所以,实际上标记函数staticinline 都没有任何用处。它应该是static不是最喜欢)或inline最喜欢),
那么,在函数上同时使用staticinline 是否实际上 没用?

【问题讨论】:

  • sn-p“以及这些函数的每个实例都有自己的静态局部变量和字符串文字的副本”来自哪里?
  • 标准是这样说的吗>>如果需要的话,通常任何主流编译器都会在调用点替换函数体,所以仅仅为#1标记函数内联并不是真正需要的。我>
  • @JonasByström: § 7.1.3/4 "如果一个具有*外部链接的函数被声明为内联一个翻译单元,它应在其出现的所有翻译单元中声明为内联;不需要诊断。内联函数带有外部链接应在所有翻译单元中具有相同的地址翻译单元。extern inline 函数中的静态局部变量始终引用同一个对象。extern inline 函数是不同翻译单元中的同一个对象*”。标记为static 的函数没有外部链接,因此反向应用。
  • @Nawaz:不,这不是标准所说的(我无处声称它确实如此),我是这么说的,它基于大多数主流编译器的行为。通常这个标准不会说任何关于实现它只谈论可观察的行为这个Q是关于实用性的。
  • 为了清楚起见,我想补充一下,这个问题是关于 static inline 不是类方法的函数。对于staticinline方法,请查看here

标签: c++ static inline


【解决方案1】:

您的分析是正确的,但并不一定意味着无用。即使大多数编译器都会自动内联函数(原因 #1),最好声明 inline 只是为了描述意图。

忽略与inlinestatic 函数的交互,应谨慎使用。命名空间范围内的 static 修饰符以前被弃用,取而代之的是未命名的命名空间 (C++03 §D.2)。由于某些模糊的原因,我不记得它在 C++11 中已从弃用中删除,但您应该很少需要它。

因此,实际上将函数标记为静态和内联都没有任何用处。它应该是静态的(不是最喜欢的)或内联的(最喜欢的),

没有偏好的概念。 static 意味着具有相同签名的不同函数可能存在于不同的.cpp 文件(翻译单元)中。 inline 不带static 表示不同的翻译单元可以定义相同的函数定义相同。

首选是使用未命名的命名空间而不是static

namespace {
    inline void better(); // give the function a unique name
}

static inline void worse(); // kludge the linker to allowing duplicates

【讨论】:

  • @Als:无论它出现在哪里,它对代码的维护者 更有用,以便声明代码的预期或假定行为。对于代码的用户来说,它并不是那么有用——函数是否内联(理想情况下)对他们来说不应该是重要的。
  • @Als: staticnamespace{} (大致)做同样的事情。 inline 做别的事情。不要低估描述意图的价值。如果您必须有效果,inline 声明可能会使编译器更积极地进行内联。从您目前所说的来看,听起来您可能一直在将这些说明符用于在 .h 中声明和在 .cpp 中定义的函数,这是错误的。
  • 不存在未命名命名空间优于静态命名空间的概念。两者都具有相同的链接效果。
  • @JohannesSchaub-litb 好吧,它们不一定完全相同。它们在 C++ 中的 TU 之间实现了相同的可见性,但 ABI/二进制格式效果可能不同。 static 不能保证生成一个可见的、唯一的名称,如 namespace{} 是。不弃用的原因是什么?是 ADL 名称关联,需要具有语言链接功能,还是其他什么?如果是 ADL,这不是由 inline 命名空间解决的吗?
  • “首选的是使用未命名的命名空间而不是静态的:” - 由谁首选,为什么?这两个版本都不应生成在 TU 之外可见的名称。
【解决方案2】:

我可能对此并不完全正确,但据我所知,声明一个函数 static inline 是使(或允许)编译器生成一个机器代码的唯一方法,而该函数实际上没有在完全编译过的代码,而您所拥有的只是将函数调用直接替换为一系列指令,就像它只是一个常规的过程体一样,在与该函数定义相关的过程调用的机器代码中没有任何痕迹源代码。

也就是说,只有static inline才能真正替代宏的使用,inline本身是不够的。

简单的 Google 搜索“静态内联”将显示有关它的编译器文档页面。我想这应该足以回答你的问题,并说,“不,它实际上并不是没用的”。这是一个讨论inline,特别是static inlinehttp://www.greenend.org.uk/rjk/tech/inline.html使用的网站示例

【讨论】:

  • 有趣的是,在此linux kernel style guide 下特别推荐您使用static inline 作为宏替代品的建议。 (“静态内联函数比宏更受欢迎。它们提供类型安全,没有长度限制,没有格式限制,并且在 gcc 下它们和宏一样便宜。”)。
  • C 中的 inline 关键字与 C++ 的语义不同。在 C 中使用带有外部链接的内联函数更加复杂,因为您必须手动选择放置非内联版本的翻译单元/目标文件。我相信使用静态内联的推荐是以 C 为中心的,并且主要是出于方便的动机。大概会增加二进制代码重复的风险
  • 如果要替换宏,extern inline 更合适。不同之处在于static inline 制作了一个非内联版本,编译器可以选择使用其中任何一个。使用extern inline,甚至不存在非内联版本。
【解决方案3】:

静态和内联是正交的(独立的)。静态意味着函数不应该在翻译单元之外可见,内联是对编译器的提示,程序员希望内联这个函数。这两个没有关系。

当内联函数不在翻译单元之外使用时,使用static inline 是有意义的。通过使用它,您可以通过在另一个翻译单元中使用相同名称命名另一个内联函数来防止意外违反 ODR 规则的情况。

例子:

source1.cpp:

inline int Foo()
{
  return 1;
}

int Bar1()
{
  return Foo();
}

source2.cpp:

inline int Foo()
{
  return 2;
}

int Bar2()
{
  return Foo();
}

如果不在 Foo 上使用静态(或不使用匿名命名空间,这是大多数 C++ 程序员的首选方式),此示例违反了 ODR,并且结果未定义。您可以使用 Visual Studio 测试 Bar1/Bar2 的结果将取决于编译器设置 - 在调试配置中,Bar1 和 Bar2 都将返回相同的值(不使用内联,链接器随机选择一个实现),在发布配置中它们每个将返回预期值。

【讨论】:

  • “当内联函数不在翻译单元之外使用时,使用static inline 是有意义的。” static 有意义,添加inline 毫无意义。
【解决方案4】:

我刚刚阅读了 gcc 的手册页,它特别说明了使用带有编译器标志的静态内联。在标志的情况下,它内联函数,如果它也是静态的并且在它被调用的每个实例中都内联,那么它会摆脱在创建的目标文件中永远不会使用的函数定义,从而减少生成代码的大小。

【讨论】:

  • ... 还内联代码被调用的任何地方,因此将生成的代码的大小增加了 that 位。没有?
【解决方案5】:

如果您谈论 free 函数(namespace 范围),那么您的假设是正确的。 static inline 函数确实没有多大价值。所以static inline 只是一个static 函数,它自动满足ODR,而inline 对于ODR 目的是多余的。

但是,当我们谈论 member 方法(class 范围)时,static inline 函数确实具有价值。
一旦您将class 方法声明为inline,它的全部内容必须对包括class 在内的所有翻译单元可见。

请记住,static 关键字对于 class 有不同的含义。
编辑:您可能知道 static 函数在 class 中没有t 具有内部链接,换句话说一个类不能有其static 方法的不同副本,具体取决于翻译 (.cpp) 单位。
但是namespace/global 范围内的免费static 函数确实每个翻译单元都有不同的副本。

例如

// file.h
static void foo () {}
struct A {
  static void foo () {}
};

// file1.cpp
#include"file.h"
void x1 ()
{
  foo();  // different function exclusive to file1.cpp
  A::foo();  // same function
}

// file2.cpp
#include"file.h"
void x2 ()
{
  foo();  // different function exclusive to file2.cpp
  A::foo();  // same function
}

【讨论】:

  • 看不懂你的回答,能否详细说明什么不同,如何不同?
  • 如果不同意,内联是没用的。它“只是”一个提示,但它仍然是一个提示,编译器会为这个提示提供一些价值。
  • "编译器通常会忽略该提示" 这取决于编译器和使用的设置。当您使用带有 /Ob1 的 Visual Studio 时,编译器会遵循提示。
  • @Suma,你的意思是说,如果你提示inline,那么MSVS 也会用/Ob1 内联一个递归函数调用(OP 中的#1 效果)?不可能。可能还有其他这样的情况,函数不会被它的定义替换(例如,函数体太大并且在不同的地方被调用)。没有任何提示的价值。 inline 的唯一保证效果是#2(来自 OP)。
  • 使用 /Ob1 inline 暗示该函数可以被内联。如果没有被标记为内联,则函数永远不会内联。
猜你喜欢
  • 1970-01-01
  • 2021-07-12
  • 1970-01-01
  • 2015-07-23
  • 2021-11-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多