什么时候应该为函数/方法编写关键字“inline”?

什么时候应该在C ++中为函数/方法inline编写关键字?

看到一些答案后,有一些相关的问题:

  • 什么时候我应该在C ++中为函数/方法编写关键字'inline'?

  • 编译器什么时候不知道何时做一个函数/方法“内联”?

  • 当为函数/方法写入“内联”时,如果应用程序是multithreading的 ,这是否重要?

哦,我的一个宠物小偷。

inline更像staticextern不是告诉编译器内联函数的指令。 externstaticinline是连接指令,几乎完全由连接器使用,而不是编译器。

据说inline暗示编译器,你认为这个函数应该被内联。 这在1998年可能是真的,但十年之后编译器不需要这样的提示。 更不用说人类在优化代码方面通常是错误的,所以绝大多数编译器都忽略了'提示'。

  • static – variables/函数名称不能用于其他编译单元。 链接器需要确保它不会意外地从另一个编译单元中使用静态定义的variables/函数。

  • extern – 在这个编译单元中使用这个variables/函数的名字,但不要抱怨,如果没有定义。 链接器会对其进行sorting,并确保所有试图使用某个外部符号的代码都有其地址。

  • inline – 这个函数将在多个编译单元中定义,不用担心。 链接器需要确保所有编译单元使用variables/函数的单个实例。

注意:一般来说,声明模板inline是没有意义的,因为它们已经具有inline的链接语义。 然而,模板的explicit特殊化和实例化需要使用inline


您的问题的具体答案:

  • 什么时候应该在C ++中为函数/方法编写关键字“inline”?

    只有当你想在头文件中定义函数的时候。 更确切地说,只有当函数的定义可以在多个编译单元中显示时才是如此。 在头文件中定义小(如在一行)函数是一个好主意,因为它为编译器提供了更多的信息来优化代码。 这也增加了编译时间。

  • 什么时候我不应该在C ++中为函数/方法编写关键字'inline'?

    不要只是因为你认为你的代码运行速度更快,如果编译器将其内联,添加内联。

  • 编译器什么时候不知道何时做一个函数/方法“内联”?

    一般来说,编译器将能够比你做得更好。 但是,如果没有函数定义,编译器就没有内联代码的选项。 在最大限度优化的代码中,通常所有private方法都是内联的,不pipe你是否要求。

    除了防止在GCC中内联,使用__attribute__(( noinline )) ,并在Visual Studio中,使用__declspec(noinline)

  • 当为函数/方法写入“内联”时,如果应用程序是multithreading的,这是否重要?

    multithreading不以任何方式影响内联。

在做模板专业化的时候,你仍然需要明确地内联你的函数(如果专精是在.h文件中)

1)现在,几乎从来没有。 如果内联一个函数是一个好主意,编译器会在没有你帮助的情况下执行。

2)总是。 见#1。

(编辑,以反映你把你的问题分成两个问题…)

什么时候我不应该在C ++中为函数/方法编写关键字'inline'?

如果函数是在.cpp文件中定义的,则不应写入关键字。

编译器什么时候不知道何时做一个函数/方法“内联”?

没有这种情况。 编译器不能内联函数。 它所能做的只是内联一些或全部的函数调用。 如果它没有获得函数的代码,则不能这样做(在这种情况下,链接器需要这样做的话)。

当为函数/方法写入“内联”时,如果应用程序是multithreading的,这是否重要?

不,这根本不重要。

我希望通过一个令人信服的例子来帮助解决这个问题的所有重要问题,以消除所有的误解。

给定两个源文件,例如:

  • inline111.cpp:

     #include <iostream> void bar(); inline int fun() { return 111; } int main() { std::cout << "inline111: fun() = " << fun() << ", &fun = " << (void*) &fun; bar(); } 
  • inline222.cpp:

     #include <iostream> inline int fun() { return 222; } void bar() { std::cout << "inline222: fun() = " << fun() << ", &fun = " << (void*) &fun; } 

  • 案例A:

    编译

     g++ -std=c++11 inline111.cpp inline222.cpp 

    输出

     inline111: fun() = 111, &fun = 0x4029a0 inline222: fun() = 111, &fun = 0x4029a0 

    讨论

    1. 即使你的内联函数应该有相同的定义,如果不是这种情况,C ++编译器也不会标记它(事实上,由于单独的编译,它没有办法检查它)。 确保这一点是你自己的责任!

    2. 链接器不会抱怨One Definition Rule ,因为fun()被声明为inline 。 但是,因为inline111.cpp是编译器处理的第一个翻译单元(实际上调用了fun() ),编译器在inline111.cpp中 第一次调用相遇时实例化fun() 。 如果编译器决定在你的程序的其他任何地方( 例如 inline222.cpp )决定调用fun()来调用fun() ,那么对fun()的调用将始终与其从inline111.cpp (调用fun()inline222.cpp中也可能会在该翻译单元中生成一个实例,但它将保持不链接)。 事实上,从相同的&fun = 0x4029a0打印输出中可以明显看出。

    3. 最后,尽pipe编译器提供了inlinebuild议来实际扩展单线fun() ,但它完全忽略了您的build议,因为两行中的fun() = 111都是明确的。


  • 案例B:

    编译 (通知反向顺序)

     g++ -std=c++11 inline222.cpp inline111.cpp 

    输出

     inline111: fun() = 222, &fun = 0x402980 inline222: fun() = 222, &fun = 0x402980 

    讨论

    1. 这个案例说明了案例A中讨论过的内容

    2. 注意一点很重要,如果你在inline222.cpp注释掉实际对fun()调用例如完全注释掉inline222.cpp中的cout -statement),那么尽pipe编译单元的编译顺序是fun()inline111.cpp中的第一次调用遇到实例时,导致打印出案例B inline111: fun() = 111, &fun = 0x402980


  • 案例C:

    编译 (通知-O2)

     g++ -std=c++11 -O2 inline222.cpp inline111.cpp 

    要么

     g++ -std=c++11 -O2 inline111.cpp inline222.cpp 

    输出

     inline111: fun() = 111, &fun = 0x402900 inline222: fun() = 222, &fun = 0x402900 

    讨论

    1. 如此处所述 , -O2优化鼓励编译器实际扩展可以内联的函数(另请注意, -fno-inline 默认没有优化选项)。 从这里的outprint可以看出, fun()实际上是内联的 (根据在特定翻译单元中的定义fun() 内联的扩展 ,导致了两个不同的 fun()打印输出。 尽pipe如此,仍然只有一个 fun()全局链接实例(如标准所要求的那样),从相同 &fun打印中可以看出。

事实上,几乎从来没有。 你所做的只是build议编译器使内联函数(例如,将所有的调用replace成这个函数)。 当然没有保证:编译器可能会忽略该指令。

编译器通常会很好地检测+优化这样的事情。

  • 编译器什么时候不知道何时做一个函数/方法“内联”?

这取决于使用的编译器。 不要盲目地相信,现在的编译器比人类更好地知道如何内联,你不应该因为性能的原因使用它,因为它是链接指令而不是优化提示。 虽然我同意在意识形态上这些论点正确地认识到现实可能是一个不同的事情。

在阅读了多个线程后,我好奇地尝试了内联代码的效果,结果是我对GCC有了可测量的加速,对于英特尔编译器也没有加速。

(更多细节:math模拟几乎没有定义在类之外的关键function,GCC 4.6.3(g ++ -O3),ICC 13.1.0(icpc -O3);通过内联添加到临界点,使GCC代码加速+ 6%。

因此,如果您将GCC 4.6限定为现代编译器,那么结果是,如果您编写CPU密集型任务并知道确切的瓶颈,则内联指令仍然很重要。

默认情况下,gcc在没有启用优化的情况下编译时不会内联任何函数。 我不知道关于visual studio – deft_code

我通过使用/ FAcs编译并查看汇编代码,检查了Visual Studio 9(15.00.30729.01):编译器在debugging模式下调用了未启用优化的成员函数调用。 即使该函数用__forceinline标记,也不会生成内联运行时代码。

你想把它放在一开始,在返回types之前。 但大多数编译器忽略它。 如果定义了它,并且它有一小段代码,大多数编译器都认为它是内联的。

在开发和debugging代码时,请将inline列出。 debugging复杂化。

添加它们的主要原因是帮助优化生成的代码。 通常这会增加代码空间以提高速度,但有时inline节省代码空间和执行时间。

在algorithm完成之前考虑到性能优化的这种思想是过早的优化 。

当应该内联:

1,当要调用函数parameter passing,控制传递,控制返回等时,要避免事情发生的开销。

2.function应该小,经常调用,并且按照80-20规则做内联是非常有利的,尽量使那些对程序性能有重大影响的函数内联。

正如我们所知道的,内联只是一个类似于注册的编译器的请求,并且它将以对象代码大小为代价。

除非你正在编写一个库或有特殊的原因,否则你可以忘掉inline并使用链接时优化 。 它删除了一个函数定义必须在标题中的要求,以便在跨编译单元进行内联时,这正是inline允许的内容。

(但看看有什么理由不使用链接时间优化? )