内联的目的是什么?

时间:2010-09-05 17:44:16

标签: c++ compiler-construction inline

关于关键字inline,我有discussion Johannes Schaub {{}}}。 那里的代码是这样的:

namespace ... {
    static void someFunction() {
        MYCLASS::GetInstance()->someFunction();
    }
};

他说:

  

将此作为内联函数可以   在可执行文件中保存代码大小

但根据我的调查结果herehere,我们不需要,因为:

  • [内联]仅在编译器的成本/收益分析显示为有利可图时才会出现
  • Microsoft Visual C ++和GCC等主流C ++编译器支持一个选项,允许编译器自动内联任何合适的函数,甚至那些未标记为内联函数的函数。
然而,约翰内斯表示明确指定它还有其他好处。不幸的是我不理解他们。例如,他说和“inline”允许你在程序中多次定义函数。,我很难理解(并找到参考资料)。

所以

  1. inline只是对编译器的推荐吗?
  2. 如果你有一个小功能(我猜1-4指令?)
  3. ,是否应该明确说明
  4. 撰写inline时还有哪些其他好处?
  5. 是否需要声明inline以减少可执行文件的大小,即使编译器(根据维基百科[我知道,错误的引用])应该自己找到这样的函数?
  6. 还有什么我想念的吗?

9 个答案:

答案 0 :(得分:6)

重申我在那些小评论框中所说的话。特别是,我从未谈论过inlin- ing

// foo.h:
static void f() {
  // code that can't be inlined
}

// TU1 calls f
// TU2 calls f

现在,TU1和TU2都有自己的f副本 - f的代码在可执行文件中两次。

// foo.h:
inline void f() {
  // code that can't be inlined
}

// TU1 calls f
// TU2 calls f

两个TU都会发出特殊标记的f版本,这些版本由链接器有效合并,丢弃除了其中一个之外的所有版本。 f的代码仅在可执行文件中存在一次。

因此我们在可执行文件中节省了空间。

答案 1 :(得分:3)

  

内联只是对编译器的推荐吗?

是。但是如果函数有多个定义(见下文),链接器需要它

  

当你有一个小函数时,我是否应该明确说明(我猜1-4指令?)

在头文件中定义的函数(通常)需要它。将它添加到小功能中并没有什么坏处(但我不打扰)。注意类声明中定义的类成员将自动声明为内联。

  

内联写作有哪些其他好处?

如果使用正确,它将停止链接器错误。

  

是否需要声明内联以减少可执行文件的大小,即使编译器(根据维基百科[我知道,错误引用])应该自己找到这样的函数?

没有。编译器对每个函数调用进行内联的成本/收益比较并做出适当的选择。因此,对于函数的调用可能会在窗帘情况下内联,而不会在其他情况下内联(取决于编译器算法的工作方式)。

速度/空间是两个相互竞争的力量,它取决于编译器正在优化的内容,这将决定天气功能的内联和天气可执行文件的增长或缩小。

另请注意,如果使用过度激进的内联导致程序扩展太多,则引用的位置会丢失,这实际上会降低程序速度(因为需要将更多可执行页面带入内存)。

多重定义:

文件:head.h

// Without inline the linker will choke.
/*inline*/       int  add(int x, int y) { return x + y; }
extern void test()

文件:main.cpp

#include "head.h"
#include <iostream>

int main()
{
    std::cout << add(2,3) << std::endl;
    test();
}

文件:test.cpp

#include "head.h"
#include <iostream>

void test()
{
    std::cout << add(2,3) << std::endl;
}

这里我们有两个add()定义。一个在main.o,一个在test.o

答案 2 :(得分:3)

  

内联只是对编译器的推荐吗?

是。

  

7.1.2函数说明符

     

2 带有内联说明符的函数声明(8.3.5,9.3,11.4)声明了一个内联函数。内联   说明符向实现指示在调用点处函数体的内联替换   比通常的函数调用机制更受欢迎。执行此操作不需要实现   在呼叫点进行内联替换;但是,即使省略了这种内联替换,其他规则也是如此   对于7.1.2定义的内联函数,仍应予以遵守。

例如来自MSDN:

  

编译器将内联扩展选项和关键字视为建议。无法保证内联函数。即使使用__forceinline关键字,也无法强制编译器内联特定函数。使用/ clr进行编译时,如果有函数应用了安全属性,编译器将不会内联函数。

请注意:

  

3.2一个定义规则

     

3 [...]内联函数应在每个使用它的翻译单元中定义。

     

4 内联函数应在每个使用它的翻译单元中定义,并且应具有准确的功能   在每种情况下(3.2)都有相同的定义。 [注意:在它之前可能会遇到对内联函数的调用   定义出现在翻译单元中。 -end note]如果函数的定义出现在翻译中   该单元在第一次声明为内联之前,该程序是不正确的。 如果是具有外部链接的函数   在一个翻译单元中内联声明,应在其出现的所有翻译单元中内联声明;   不需要诊断。 具有外部链接的内联函数应具有相同的地址   翻译单位。外部内联函数中的静态局部变量始终引用同一个对象。   extern内联函数体中的字符串文字是不同翻译单元中的相同对象。   [注意:出现在默认参数表达式中的字符串文字不在内联函数的主体中   仅仅因为表达式用于来自该内联函数的函数调用。 - 末端注意]一种类型   在extern内联函数体内定义的是每个翻译单元中的相同类型。

[注意:强调我的]

TU基本上是一组标题加上一个实现文件(.cpp),它导致一个目标文件。

  

如果你有一个小功能(I   猜1-4指令?)

绝对。为什么不帮助编译器帮助您生成更少的代码?通常,如果prolog / epilog部分产生的成本高于内联强制编译器生成它们?但是你必须绝对必须在开始内联之前阅读这篇GOTW文章:GotW #33: Inline

  

内联写作有哪些其他好处?

  • namespace也可以是inline。请注意,默认情况下,类主体中定义的成员函数是内联的。因此隐式生成特殊成员函数。

  • 无法在实现文件中定义函数模板(请参阅FAQ 35.12),除非您提供显式实例化(对于使用该模板的所有类型 - 通常是PITA IMO)。请参阅Moving Templates Out of Header Files上的DDJ文章(如果您对标准中删除的export关键字上的其他文章感到奇怪,请阅读。

  

是否需要声明内联以减少可执行文件   大小,即使编译器   (根据维基百科[我知道,不好   参考])应该找到这样的功能   本身?

同样,正如我所说,作为一名优秀的程序员,你应该尽可能地帮助编译器。但是here's C ++常见问题解答提供了inline的内容。所以要小心。并非所有编译器都进行此类分析,因此您应该阅读有关其优化开关的文档。例如:GCC做了类似的事情:

  

您还可以指示GCC尝试使用选项-finline-functions将所有“足够简单”的功能集成到其调用者中。

大多数编译器允许您在某种程度上覆盖编译器的成本/效益比分析。 MSDNGCC文档值得一读。

答案 3 :(得分:2)

实际上,内联函数可能会增加可执行文件的大小,因为在调用此函数的每个位置都会复制内联函数代码。使用现代C ++编译器,内联主要允许程序员相信,他编写高性能代码。编译器决定是否使函数内联。所以,写内联只会让我们感觉更好......

答案 4 :(得分:2)

  1. 是。仅此而已。
  2. 没有
  3. 你提示编译器它是一个被称为很多的函数,其中跳转到函数部分需要花费大量的执行时间。 编译器可能决定将函数代码放在调用它的位置,而不是正常函数所在的位置。但是,如果函数在x个位置内联,则需要x倍于正常函数的空间。
  4. 始终相信您的编译器在过早微优化的主题上比您自己更聪明。

答案 5 :(得分:1)

对此:

  

“inline”允许您在程序中多次定义函数。

我可以想到一个有用的实例:使复制保护代码更难破解。如果你有一个程序来获取用户信息并根据注册密钥对其进行验证,那么内联执行验证的功能将使破解者更难找到该功能的所有重复项。

至于其他方面:

  1. inline只是对编译器的推荐,但有#pragma个指令可以强制内联任何函数。
  2. 由于这只是一个建议,因此明确要求它并让编译器覆盖您的建议可能是安全的。但是最好省略它并让编译器决定。
  3. 上面提到的混淆是内联的一个可能的好处。
  4. 正如其他人所提到的,inline实际上会增加已编译代码的大小。

答案 6 :(得分:1)

  1. 是的,当它认为函数太大或使用不兼容的功能(可能是异常处理)时,它会很容易忽略它。此外,通常有一个编译器设置让它自动内联它认为值得的函数(MSVC中的/ Ob2)。

  2. 如果将函数的定义放在头文件中,则应明确说明。通常需要确保多个翻译单元可以利用它。并避免多重定义错误。此外,内联函数放在COMDAT部分中。这告诉链接器它只能选择多个定义中的一个。相当于MSVC中的__declspec(selectany)。

  3. 内联函数通常不会使可执行文件变小。由于调用操作码通常小于内联加工代码,但非常小的属性访问器样式函数除外。这取决于但更大并不是一个不寻常的结果。

答案 7 :(得分:1)

内联的另一个好处(请注意,当函数使用引用参数时,实际内联有时与使用“内联”指令正交)。将两个变量传递给非内联函数以将其第一个操作数添加到第二个操作数将需要推送第一个操作数的值和第二个操作数的地址,然后调用一个必须弹出第一个操作数和第二个操作数的函数。 ,然后间接将前一个值添加到弹出地址。如果函数是内联扩展的,编译器可以直接将一个变量添加到另一个变量中。

答案 8 :(得分:0)

实际上,内联会导致更大的可执行文件,而不是更小的可执行文件。 通过粘贴功能代码来减少一个间接级别。

http://www.parashift.com/c++-faq-lite/inline-functions.html