【问题标题】:Inline functions across multiple C++ translation units跨多个 C++ 翻译单元的内联函数
【发布时间】:2020-10-16 11:53:06
【问题描述】:

我最近了解了 C++ 中的内联函数。我了解它们是如何工作的(在扩展代码而不是进行跳转等方面),但是当涉及到多个翻译单元/*.cpp 文件时我应该如何使用它们有点困惑。

我已经读过,根据经验,我应该在头文件中包含函数本身的定义(而不仅仅是它的声明),然后在我打算使用的每个翻译单元中包含头文件内联函数。这对于内联函数来说不是问题,因为它只会在每个翻译中独立存在并在需要时/如果需要(由编译器决定)自行扩展[如果我对此有任何错误,请纠正我!]

但是,我还读到,在链接期间,即使内联函数本身的定义不存在于头文件中,链接器也可以找到它的定义并在需要时对其进行扩展。但是链接器如何能够从不同的翻译单元扩展功能呢?如果是这样的话,为什么我需要在每个翻译单元中包含函数的定义(通过头文件)?

【问题讨论】:

  • “内联”有两种含义,我不确定您是否知道其中的区别。编译器是否内联函数调用现在完全取决于编译器(与inline 关键字无关)。您可以给编译器一个提示,但是编译器只是忽略它,因为它更清楚内联什么和不内联。 inline 关键字是为了能够在 headers 中定义函数
  • 请对Interprocedural optimization做一些研究。尤其是 LTO(链接时间优化)。
  • 我确实不知道这个@idclev463035818 但是,如果我在头文件中将函数定义为内联函数,它现在会在每个函数调用上扩展(仍然)而不是每次都跳转?另外,我仍然有点困惑链接器如何在链接期间内联定义为不同翻译单元的函数? (假设我们只在其中一个转换单元中定义了函数)
  • 如果您在标头中将函数声明为inline,那么它可能是内联的,也可能不是内联的;)我知道这很混乱,这是由于历史原因。过去inline 确实是用来判断方法是否应该在每次调用时内联。

标签: c++ inline


【解决方案1】:

函数前面的inline 关键字只是编译器的可选指示符,首选内联。现代编译器在代码内联时并不关心 inline 关键字。

inline 的重要含义是“允许多个定义”,如果成员函数定义在类的主体中,则它们会隐式内联。

如果您在多个翻译单元中具有相同的函数定义,则需要inline(例如,如果您将函数定义放在标头中,并且该标头包含在多个 cpp 文件中)

我已经读过,根据经验,我应该在头文件中包含函数本身的定义(而不仅仅是它的声明),然后在我打算使用的每个翻译单元中包含头文件内联函数。这对于内联函数来说不是问题,因为它只会在每个翻译中独立存在,并在需要时/如果需要(由编译器决定)自行扩展

将函数定义放在头文件中可以使工具链更容易进行内联,因为它在使用时知道其定义并且可以在编译步骤中执行内联。在多个翻译单元中有一个函数定义需要用inline 标记。

但是,我还读到,在链接期间,即使内联函数本身的定义不存在于头文件中,链接器也可以找到它的定义并在需要时对其进行扩展。但是链接器如何能够从不同的翻译单元扩展函数呢?

工具链可以为任何函数(无论它们的定义在编译时是否已知)进行链接时优化(如内联),其定义在链接时已知(静态链接),但链接时优化倾向于到 - 至少在过去 - 不如编译时优化那么有效。

链接时优化的问题在于,该工具链要么需要跟踪源代码提供的有助于优化的附加信息,要么需要依赖可能不太强大的策略,您可以在二进制文件上应用。

如果您有一个看起来像这样的main.cpp

int sum(int a, int b) {
  return a+b;
}

int main() {
  std::cout << sum(3,4) << std::endl;
  return 0;
}

任何现代编译器都会内联该代码(或者在这种情况下将其完全优化为std::cout &lt;&lt; 7 &lt;&lt; std::endl;)。

这也很好,内联/优化:

sum.h

int sum(int a, int b) { // no inline keyword in front of the function
  return a+b;
}

ma​​in.cpp

#include "sum.h"
int main() {
  std::cout << sum(3,4) << std::endl;
  return 0;
}

但是,一旦您在多个 cpp 文件中使用 #include "sum.h" 并将这些翻译单元链接在一起,您就会遇到问题并需要使用 inlineinline 在函数前面实际上只是告诉链接器在多个翻译单元中具有相同的定义是你的意图。

【讨论】:

  • 我已经得到了答案,但只是为了 100% 清楚:当我在标题中将函数声明为内联时,这不会决定它是否会在每个函数调用上扩展吗?因此,例如,我可以在定义为内联的标头中拥有一个函数,并且仍然有可能(当我调用该函数时)发生跳转,而不是在我进行调用的翻译单元内部进行扩展。因此,为了更快的执行,'small function'='inline'被弃用了?
  • @pol when I declare the function as inline in the header, will that not dictate whether or not it will expand on each function call? 是的,这完全取决于编译器,它可能会也可能不会内联函数,无论它之前是否有 inline。做不做取决于编译器认为哪个更好。
  • @pol Therefore, the 'small function'= 'inline' for faster execution is deprecated? 取决于您的意思。在标头中移动一个小函数(或您喜欢内联的函数)(如果它在多个翻译单元中使用),以便编译器更有可能或更容易内联,恕我直言。如果您在标题中定义它,则需要在其前面添加 inline 关键字,以便链接器不会抱怨多个定义。如果一个函数只在你的一个 cpp 文件中使用,你可以在其中定义它,没有 inline 关键字。
  • 当没有发生扩展时,是否会发生常规跳转?因此,在没有发生内联的情况下,无论如何,该函数都需要存在于 .text 段中
  • @pol 在头文件中包含大部分定义会损害编译时间,并且根据程序的复杂性和交叉依赖关系,并非总是可行。如果您是否希望在标题中包含定义,它始终取决于确切的用例。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多