inline 说明符

整个定义都在 class/struct/union 的定义内的函数,若非被附着到具名模块, (C++20 起)都是隐式的内联函数,无论它是成员函数还是非成员 friend 函数。

首个声明有 constexpr 或 consteval (C++20 起) 的函数是隐式的内联函数。

弃置的函数是隐式的内联函数:它的(弃置)定义可以在多于一个翻译单元中出现。

特征

  • 相当于把内联函数里面的内容写在调用内联函数处;
  • 相当于不用执行进入函数的步骤,直接执行函数体;
  • 相当于宏,却比宏多了类型检查,真正具有函数特性;
  • 编译器一般不内联包含循环、递归、switch 等复杂操作的内联函数;
  • 在类声明中定义的函数,除了虚函数的其他函数都会自动隐式地当成内联函数。

编译器对 inline 函数的处理步骤

  • 将 inline 函数体复制到 inline 函数调用点处;
  • 为所用 inline 函数中的局部变量分配内存空间;
  • 将 inline 函数的的输入参数和返回值映射到调用方法的局部变量空间中;
  • 如果 inline 函数有多个返回点,将其转变为 inline 函数代码块末尾的分支(使用 GOTO)。

补充

在内联函数中,
所有函数定义中的函数局部静态对象在所有翻译单元间共享(它们都指代相同的在某一个翻译单元中定义的对象)
所有函数定义中所定义的类型同样在所有翻译单元中相同。
命名空间作用域的内联 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 中不同。