【问题标题】:Inline functions in multiple translation units多个翻译单元中的内联函数
【发布时间】:2019-07-25 21:10:29
【问题描述】:

让我们从以下一组 C++ 文件开始:

// my_class.h
struct MyClass
{
  void my_func();
};

void MyClass::my_func()
{}


// f.h
void f1();
void f2();


// f1.cpp
#include "my_class.h"

void f1()
{
  MyClass a;
  a.my_func();
}


// f2.cpp
#include "my_class.h"

void f2()
{
  MyClass a;
  a.my_func();
}


// main.cpp
#include "f.h"

int main()
{
  f1();
  f2();

  return 0;
}

我试图用

编译这段代码
$ g++ f1.cpp f2.cpp main.cpp

显然,链接器抱怨重复符号my_func:

duplicate symbol __ZN7MyClass7my_funcEv in:
    /var/folders/yj/zz96q16j6vd1dq1_r3mz8hzh0000gn/T/f1-962ae7.o
    /var/folders/yj/zz96q16j6vd1dq1_r3mz8hzh0000gn/T/f2-aef78c.o
ld: 1 duplicate symbol for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

我的下一个尝试是将函数定义移动到类定义中,所以我们得到:

struct MyClass
{
  void my_func()
  {}
};

运行同样的g++ 命令,程序编译成功。这是因为在类定义中定义的函数被隐式标记为 inline (§10.1.6 * 3)。

标准规定:

带有 inline 说明符的函数声明(11.3.5、12.2.1、14.3)声明了一个内联函数。 inline 说明符向实现表明,在调用点对函数体进行内联替换优于通常的函数调用机制。在调用点执行此内联替换不需要实现;但是,即使省略了此内联替换,本节中指定的内联函数的其他规则仍应遵守

这似乎与cppreference.com 上的内容有些矛盾:

因为函数的关键字 inline 的含义是“允许多个定义”而不是“首选内联”,[...]

据我所知,在类定义中定义一个函数会隐含inline,这并不一定意味着编译器会选择内联它的主体,而是会在每个翻译单元中定义它。这是正确的吗?

第二个问题是关于模板类的。分离模板类的声明/定义是一个问题,正如here 所述,所以我们只能在类定义中包含函数定义,这使得它隐含inline,对吧?这有什么影响?

既然类是模板时我们只能选择在类定义中定义函数,那么对于非模板类该怎么办呢?我们应该在源文件中定义函数,并且尽可能只在头文件中保留声明吗?

【问题讨论】:

  • 问题是在你的情况下my_func 没有在类定义中定义。它只在那里声明。
  • @NO_NAME 第二个例子正好说明了这一点:在类定义中包含函数定义
  • “因为当类是模板时,我们只能选择在类定义中定义函数”这是不正确的,所以剩下的问题是无用的
  • “第二题来了”,请一题一题
  • @Victor 是的,不要想太多包括警卫。它们很有用,但在这个问题的上下文中毫无意义。

标签: c++


【解决方案1】:

据我所知,在类定义中定义一个函数会隐含inline,这并不一定意味着编译器会选择内联其主体,而是会在每个翻译单元中对其进行定义。这是正确的吗?

正确。当在类中定义时,它被标记为内联,并且可以将该定义带入多个翻译单元。编译器会为你处理。

第二个问题是关于模板类的。分离模板类的声明/定义是一个问题,正如here 所述,所以我们只能在类定义中包含函数定义,这使得它隐含地inline,对吧?这有什么影响?

这是不正确的。模板很特别。一旦代码被编译,它们实际上并不存在。模板是什么,是一个用于消除类或函数的方法。因此,they are implicitly inline as well 允许模板包含在每个使用它的翻译单元中,以便编译器可以根据需要从中删除具体的类/函数。这意味着您可以在类之外定义类成员函数。

既然类是模板时我们只能选择在类定义中定义函数,那么对于非模板类该怎么办呢?我们应该在源文件中定义函数,并且尽可能只在头文件中保留声明吗?

通常您希望将定义放在 cpp 文件中。从中获得的好处是,如果您更改函数的实现,您只需要重新编译一个 cpp 文件。如果它们在头文件中,那么您需要重新编译每个包含该头文件的 cpp 文件,这会导致构建时间更长。

【讨论】:

  • the template needs to be included in every translation unit that uses it - 选词不当。只需要包含模板的声明。您可以将定义放在单个翻译单元中并显式实例化它。
  • @NO_NAME 我已经更新了措辞。更好?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-12-08
  • 2019-02-02
  • 1970-01-01
相关资源
最近更新 更多