【问题标题】:Confusion with the problems of inline function对内联函数的问题感到困惑
【发布时间】:2025-11-24 13:30:01
【问题描述】:

在*的内联函数问题中: http://en.wikipedia.org/wiki/Inline_expansion#Problems

它说:“# 语言规范可能允许程序对过程的参数做出额外的假设,在过程被内联后它不能再做出这些假设。”

有人可以详细说明这一点吗?

如何防止 GCC 内联 C++ 函数?

【问题讨论】:

  • "如何防止 GCC 内联 C++ 函数?"通常,这是错误的问题。正确的问题经常是“为什么要阻止 GCC 内联 C++ 函数?”
  • 我相信原因与别名有关。当只是调用一个函数时,编译器可以忽略调用函数中的指针,这些指针可能会为被调用函数中指向的对象起别名。内联时,被调用函数的代码与调用函数的代码在同一块中执行,因此别名成为问题。
  • @litb,我还是没看到。我们能看一个具体的例子吗?
  • 这是一个具体的例子: class MyClass{ public: static MyClass* getInstance(){MyClass 我自己;返回 &myself;} }

标签: c++ optimization function inline


【解决方案1】:

在 C++ 中,inline 关键字实际上只有一个必需含义:单定义规则对于该函数被暂停(例如,该函数可以在多个翻译单元中定义,并且代码仍然符合)。

具体来说,使用inline 关键字并不能确保该函数的代码将内联生成。在类定义中定义一个函数也使它成为一个内联函数——但同样,这也不能确保它的代码也会内联生成。

相反,在类定义之外定义的没有inline 关键字的函数可以并且可能仍然内联生成其代码。唯一的区别是,在这种情况下,函数的多个定义会导致代码不一致。

底线是可移植代码不能保证代码是内联生成还是非内联生成。但是,如果您不介意让您的代码不可移植,您可以使用__attribute__(noinline)

但是,我不会根据 Wikipedia 中引用的引用来做这件事。 Wikipedia 几乎不是权威来源,即使是,您所引用的只是关于在某些假设条件下在某些假设编译器上使用某些假设语言可能会发生什么的模糊陈述。通常,您最好编写清晰易读的代码,并让编译器担心由此产生好的结果。

【讨论】:

    【解决方案2】:

    inline 关键字是对编译器的建议或刺激。*的定义似乎暗示通过使用这种关键字,您可以限制您可以使用该函数执行的操作。例如,您可能认为不可能获取内联函数的地址。相反,C++ 编译器采取相反的策略,即使函数被标记为内联,如果在代码中的某处获取了所述函数的地址,则不会内联生成该函数。

    同样,如果一个函数是虚函数,则(显然)不能内联生成,但这仍然不会使多态函数的内联定义非法。

    也许我在这里写的内容会让您深入了解编译器需要对 Coffin 雄辩地表达的 inline 关键字做什么。

    【讨论】:

      【解决方案3】:

      在如何内联函数方面,C/C++ 得到了很好的说明。因此,来自*的特定评论不适用于这些语言。

      假设 C 语言规范要求函数调用参数以相反的顺序在堆栈上传递(编辑:并且堆栈总是向下增长,并且参数之间没有填充)。实际上,它们通常是,但您不能假设这总是正确的。下面的代码在这个陌生的世界里是有效的。

      void foo( int i, int j )
      {
        int myi = &j[1];
        return myi + j;
      }
      

      如果 foo where inlined 出现在 j 之上的堆栈上的整数可能不是 i。

      【讨论】:

      • -1 在任何现实中,此代码的结果都没有在 C++ 规范中定义。很难看出您的回答将如何帮助提问者清楚地了解内联关键字的使用/误用。
      • 问题分为两部分。澄清“语言规范”注释以及如何在 c++ 中禁用内联。来自*的“语言规范”评论不适用于 c/c++,因此我发明了一种类似于 C 的语言作为示例。我绝不声称我写的是有效的 c++ 甚至 c。我试图用“假设”和“在这个陌生的世界”这两个短语来说明这一点