书接上文

简单来说我认识了一个大佬,他对于 inline 的观点是:在单个文件的编译中,从语言的角度来讲,inline没有任何作用

所以说,他认为,inline 完全没有任何作用,我们加 inline 是没有任何意义的。

对于 GCC(我们的编译器)的官方文档:

GCC does not inline any functions when not optimizing unless you specify the ‘always_inline’ attribute for the function
Translate : 你标了inline它也不会内联,它只认gnu::always_inline

在编译器开了优化时,内联不内联完全取决于编译器自己想法而不是看 inline 标记;而在编译器没有开优化时,他是不会做任何内联的;只有你指定 [[gnu::always_inline]] 才会“强制”让编译器内联

inline 的主要作用,并不是内联,而是允许重复定义

这里所谓的重复定义,指的是:

一个可执行文件,经链接后一个符号的定义只能出现一次,这叫做单一定义原则(ODR)

但 inline 除外,inline 标记的符号可以多次定义,只要每次定义的写法是完全一致的,编译文件可以通过链接并且无未定义行为。

说的是链接时,两个TU(翻译单元,也就是常说的源文件)出现指代同一实体的同名符号,视为允许的重定义

当然这里特指多文件编译,我们在单个文件内重复定义显然不论是如何都会报错的。


然后针对这个命题我们展开了激烈的讨论。我举出了一个强有力的例子:

洛谷评测记录

这里的评测记录,16s 多的是加了 inline 的代码;17s 多的是未加 inline 的代码。很显然评测机对于这个 inline 的评测特别稳定,肯定不是服务器波动导致的。

对于此,他的解释是:这个编译器喜欢inline

简单来说,inline是要求编译器“更喜欢内联的做法而非调用的做法”,编译器在优化时有个量表,你带了inline就更倾向于优化成内联而非调用。

而这个量表,在每个编译器是不一样的,所以说 inline 的作用会在不同的编译器产生不一样的效果。

这里指的不同编译器主要体现为两个方面,环境编译器版本

举个例子,在 Windows 环境下的 GCC14.1 和在 Linux 环境下的 GCC14.1 就是算作不同的编译器;在同一台电脑的同一个操作系统中的 GCC13.x 和 GCC14.x 也算作不同的编译器。

除此之外,各种电脑上的一些信息都不同,所以说他认为“这个量表是不可控的,对应的,inline的性能在各个方面的表现也是不可控的”


对于上述的言论进行总结的话,inline 是建议编译器开内联,而 [[gnu::always_inline]] 是强制编译器内联。内联了就一定 inline,inline 了却不一定内联。所以说 inline = 内联 的就都是错误的。

如果你的代码真的需要 inline 可以这么写:

1
[[gnu::always_inline]] inline void Your_func() {}

实测在下面这个编译选项中是可以正常编译通过的

1
g++ asdf.cpp -o asdf -std=c++14 -O2 "-Wl,-stack=33554432" -Wall -Wextra