内联还有用吗?

我相信, inline已经过时了,因为我在这里读到:

无论你如何将函数指定为inline ,编译器都可以忽略这个请求:编译器可能会内联展开一些全部或者没有inline

然而, Angew似乎明白我不知道的事情。 在这个问题上,他和我之间来回转换,关于inline是否仍然有用。

这个问题不是一个问题:

  • inlineinline的历史用法仍然可以用于暗示编译器inline函数: 何时应该为函数/方法编写关键字“内联”? 。
  • 内联函数代码的好处或缺点: C ++中内联函数的好处?
  • 强制编译器inline函数代码: 在其他翻译单元中强制内联函数

请记住,编译器可以随意inline ,因此inline inline在这里没有帮助: inline可以用来强制编译代码中的更改, 而不是build议更改。

我会尽我所能解释我的“秘密理解”。

这里有两个完全独立的概念。 一种是编译器能够通过在呼叫站点直接重复函数体来replace函数调用。 另一种是在多个翻译单元(=多个.cpp文件)中定义一个function的可能性。

第一个被称为函数内联。 第二个是inline关键字的用途。 从历史上看, inline关键字对编译器来说也是一个强烈的build议,即它应该内联inline标记的函数。 随着编译器在优化方面变得越来越好,这个function已经退化,并且使用inline作为内联函数的build议确实已经过时了。 如果编译器发现这是一个更好的优化,编译器会高兴地忽略它,并完全内联其他的东西。

我希望我们已经处理了明确的inline关系。 目前的代码没有。

那么, inline关键字的实际用途是什么? 很简单:一个标记为inline的函数可以在多个翻译单元中定义,而不违反一个定义规则(ODR)。 想象一下这两个文件:

file1.cpp

 int f() { return 42; } int main() { return f(); } 

file2.cpp

 int f() { return 42; } 

这个命令:

 > gcc file1.cpp file2.cpp 

会产生一个链接器错误,抱怨符号f被定义了两次。

但是,如果使用inline关键字标记函数,它会特别告诉编译器和链接器:“你们确保这个函数的多个相同的定义不会导致任何错误!

所以以下将起作用:

file1.cpp

 inline int f() { return 42; } int main() { return f(); } 

file2.cpp

 inline int f() { return 42; } 

编译和链接这两个文件不会产生任何链接错误。

注意, f的定义当然不一定要在文件中。 它可以来自#include d头文件:

f.hpp

 inline int f() { return 42; } 

file1.cpp

 #include "f.hpp" int main() { return f(); } 

file2.cpp

 #include "f.hpp" 

基本上,为了能够将一个函数定义写入一个头文件,你必须将它标记为inline ,否则将导致多重定义错误。


拼图的最后一部分是:为什么关键字实际上拼写inline时,它与内联无关? 原因很简单:为了内联函数(即通过在调用站点上重复它的主体来replace对它的调用),编译器必须首先具有该函数的主体。

C ++遵循一个单独的编译模型,其中编译器不能访问目前正在生成的目标文件以外的目标文件。 因此,为了能够内联函数,其定义必须是当前翻译单元的一部分。 如果你想能够在多个翻译单元中进行内联,其定义必须包含在所有翻译单元中。 通常情况下,这会导致多重定义错误。 所以,如果你把你的函数放在一个头文件中,并且把它的定义包含在任何地方,那么你必须将它标记为inline以防止出现多个定义错误。

请注意,即使在今天,编译器内联任何函数也是合适的,但它仍然可以访问该函数的定义。 因此,虽然inline关键字不是必需的,但请注意,如果select这样做,您仍然可能会发现需要使用它来使编译器进行内联。 没有它,你可能无法将定义放到翻译单元中,如果没有定义,编译器根本无法内联函数。

编译器不能。 链接器可以。 现代优化技术包括链接时间代码生成(也称为整体程序优化),在实际链接之前,优化器作为链接过程的一部分在所有目标文件上运行。 在这一步中,当然所有的函数定义都是可用的,并且没有在程序中的任何地方使用单个inline关键字,内联是完全可能的。 但是,这种优化通常在构build时间上是昂贵的,特别是对于大型项目。 考虑到这一点,仅仅依靠LTCG进行内联可能不是最好的select。


为了完整性,我在第一部分中略微作了一些欺骗。 ODR属性实际上不是inline关键字的属性,而是inline函数 (这是该语言的一个术语)的属性。 内联函数的规则是:

  • 可以在多个翻译单元中定义,而不会导致链接器错误
  • 必须在使用它的每个翻译单元中进行定义
  • 它的所有定义必须是令牌令牌和实体对等实体

inline关键字将函数转换为内联函数。 将函数标记为内联的另一种方法是直接在类定义中定义(而不仅仅是声明)它。 这样的函数是自动内联的,即使没有inline关键字。

inline大多只是一个外部链接说明符,因为你陈述的原因。

所以是的,它确实有用,但与实际内联函数不同。 它允许您在编译单元之间多次定义相同的方法,并将它们正确地链接在一起,而不是获取多个定义错误。

 //header.h inline void foo() {} void goo() {} //cpp1.cpp #include "header.h" //cpp2.cpp #include "header.h" // foo is okay, goo breaks the one definition rule (ODR) 

实际上,强制函数内联取决于编译器,有些可能通过特定的attributepragma或( __forceinline )或其他方法支持。

简单地说,它允许你在不破坏ODR的情况下在头文件中定义函数。