【问题标题】:Inline keyword vs header definition内联关键字与标题定义
【发布时间】:2012-04-11 09:20:54
【问题描述】:

在函数前使用 inline 关键字和在头部声明整个函数有什么区别?

所以...

int whatever() { return 4; }

.h:

inline int whatever();

.cpp:

inline int myClass::whatever()
{
    return 4;
}

就此而言,这是做什么的:

inline int whatever() { return 4; }

【问题讨论】:

    标签: c++ optimization inline


    【解决方案1】:

    有几个方面:

    语言

    • 当一个函数用 inline 关键字标记时,它的定义应该在 TU 中可用,否则程序格式不正确。
    • 在类定义中正确定义的任何函数都被隐式标记为inline
    • 标记为inline(隐式或显式)的函数可以在多个 TU 中定义(尊重 ODR),而常规函数则不然。
    • 模板函数(未完全专门化)与inline 的处理方式相同。

    编译器行为

    • 标记为inline 的函数将在每个需要的对象文件中作为弱符号发出,这可能会增加它们的大小(查找模板膨胀)。
    • 而编译器实际上内联调用(即,在使用点复制/粘贴代码而不是执行常规函数调用)完全由编译器自行决定。关键字的存在可能会或不会影响决策,但它充其量只是一个提示

    链接器行为

    • 弱符号合并在一起,在最终库中出现一次。一个好的链接器可以检查多个定义是否一致,但这不是必需的。

    【讨论】:

    • “TU”和“ODR”是什么意思?
    • @WiSaGaN: TU = Translation Unit:粗略地说,预处理的源文件。 ODR = 一个定义规则:要求函数/类的所有定义在字符级别和 TU 之间都应该相同。
    • 在字符级别还是在令牌级别?
    • @FredOverflow: Tokens 在正式定义中。如果您查看 §3.2/6,您还会注意到名称查找的要求和有关模板的其他改进。 ODR 是一头复杂的野兽:x
    • @DavidKernin:如果一个模板函数是完全特化的,那么它就不再是“通用”并且类似于一个“常规”函数,因此编译器不需要发出一个弱符号,除非你标记它@ 987654327@.
    【解决方案2】:

    没有inline,如果函数在命名空间或全局范围内声明(导致链接器错误),您可能会得到多个导出的符号。

    但是,对于一个类(如您的示例所示),大多数编译器都会隐式声明该方法为内联(-fno-default-inline 将在 GCC 上禁用该默认值)。

    如果您将函数声明为内联函数,编译器可能希望在翻译中看到它的定义。因此,您应该在定义可见时保留它。

    在更高级别:类声明中的定义通常对更多翻译可见。这可以带来更好的优化,并且可以增加编译时间。

    除非手动优化和快速编译都很重要,否则这些天在类声明中使用关键字是不寻常的。

    【讨论】:

      【解决方案3】:

      inline 的目的是允许在多个翻译单元中定义一个函数,这对于一些编译器来说是必要的,以便能够在任何使用它的地方内联它。每当您在头文件中定义函数时都应该使用它,尽管您可以在定义模板或类定义中的函数时省略它。

      在没有inline 的标题中定义它是一个非常糟糕的主意;如果您包含来自多个翻译单元的标题,则您违反了一个定义规则;您的代码可能不会链接,并且如果链接可能会表现出未定义的行为。

      在带有inline 的标头中声明它但在源文件中定义它也是一个非常糟糕的主意;该定义必须在任何使用它的翻译单元中可用,但通过在源文件中定义它只能在一个翻译单元中使用。如果另一个源文件包含头文件并尝试调用该函数,则您的程序无效。

      【讨论】:

        【解决方案4】:

        这个问题解释了很多关于内联函数 What does __inline__ mean ? 的内容(尽管它是关于 inline 关键字的。)

        基本上,它与标题无关。在头文件中声明整个函数只会更改函数源所在的源文件。 inline 关键字修改 生成的编译函数 的放置位置 - 在它自己的位置,以便每次调用会去那里,或者代替每个电话(更好的性能)。然而,编译器有时会为自己选择内联的函数或方法,而关键字只是编译器的建议。即使没有指定内联的函数也可以被编译器选择为内联,如果这样可以提供更好的性能。

        【讨论】:

          【解决方案5】:

          如果您将多个对象链接到一个可执行文件中,通常应该只有一个对象包含函数的定义。对于int whatever() { return 4; } - 任何用于生成对象的翻译单元都将包含whatever 函数的定义(即可执行代码)。链接器不知道将调用者定向到哪一个。如果提供了inline,那么可执行代码可能会或可能不会在调用站点内联,但如果不是,则允许链接器假定所有定义都相同,并任意选择一个来引导调用者。如果不知何故定义不一样,那么它被认为是你的错,你会得到未定义的行为。要使用inline,必须在编译调用时知道定义,因此您将内联声明放在头文件中并将内联定义放在 .cpp 文件中的想法只有在所有调用者碰巧在同一时间之后才有效.cpp 文件 - 通常它已损坏,并且您希望(名义上)内联函数的定义出现在声明它的标头中(或存在没有事先声明的单个定义)。

          【讨论】:

            猜你喜欢
            • 2015-01-18
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多