huihut/interview

C++ 的inline 远不止如此

Mq-b opened this issue · 3 comments

Mq-b commented

在内联函数中,

  • 所有函数定义中的函数局部静态对象在所有翻译单元间共享(它们都指代相同的在某一个翻译单元中定义的对象)
  • 所有函数定义中所定义的类型同样在所有翻译单元中相同。
命名空间作用域的内联 const 变量默认具有外部链接(这点与非内联非 volatile 的有 const 限定的变量不同) (C++17 起)


在内联函数中,

所有函数定义中的函数局部静态对象在所有翻译单元间共享(它们都指代相同的在某一个翻译单元中定义的对象)
所有函数定义中所定义的类型同样在所有翻译单元中相同。
命名空间作用域的内联 const 变量默认具有外部链接(这点与非内联非 volatile 的有 const 限定的变量不同)

(C++17 起)
inline 关键词的本意是作为给优化器的指示器,以指示优先采用函数的内联替换而非进行函数调用,即并不执行将控制转移到函数体内的函数调用 CPU 指令,而是代之以执行函数体的一份副本而无需生成调用。这会避免函数调用的开销(传递实参及返回结果),但它可能导致更大的可执行文件,因为函数体必须被复制多次。

因为关键词 inline 的含义是非强制的,编译器拥有对任何未标记为 inline 的函数使用内联替换的自由,和对任何标记为 inline 的函数生成函数调用的自由。这些优化选择不改变上述关于多个定义和共享静态变量的规则。

由于关键词 inline 对于函数的含义已经变为“容许多次定义”而不是“优先内联”,因此这个含义也扩展到了变量。

(C++17 起)

即使是 C 语言的 inline 也有比这多的多的意思。编译器优化层面的理解反而是最简单的。

不管是否进行内联,内联函数都保证下列语义:

任何拥有内部链接的函数都可以声明成 static inline ,没有其他限制。

一个非 static 的内联函数不能定义一个非 const 的函数局部 static 对象,并且不能使用文件作用域的 static 对象。

若非 static 函数声明为 inline ,则必须在同一翻译单元中定义它。不使用 extern 的内联定义不会对外部可见,而且不会阻止其他翻译单元定义同一函数。这使得 inline 关键词成了 static 外另一种在头文件定义函数的方式,可以由同一程序的多个翻译单元包含该头文件。

若函数在一些翻译单元中声明为 inline ,它就不需要在处处皆声明为 inline :至多一个单元会提供常规的非 inline 非 static 函数,或是声明为 extern inline 的函数。称此翻译单元提供外部定义。为避免未定义行为,若在表达式中使用拥有外部链接的函数名,则程序中必须存在一个外部定义,见一个定义规则

内联函数的地址始终是外部定义的地址,但当以此地址进行函数调用时,调用内联定义(若存在于翻译单元中)还是外部定义是未指定的。定义于内联定义中的 static 对象与定义于外部定义中的 static 对象有别:

inline 关键词是从 C++ 吸收的,但在 C++ 中,若函数声明为内联,则它必须在每一个翻译单元声明为内联,而且每一个内联函数都必须有准确相同的定义( C 中,定义可以相异,而依赖这些差异仅导致未指定行为)。另一方面, C++ 允许非 const 的函数局域 static 对象,而且所有来自一个内联函数不同定义版本的函数局部 static 对象都相同,但它们在 C 中不同。