为什么有时需要在头文件中放入c ++模板函数定义?

时间:2018-06-23 22:25:32

标签: c++ templates

所以我在c ++项目中遇到了链接错误,并且有一个stackoverflow帖子提到,模板方法也需要在标头中定义,而不是在源文件中定义。

该特定方法的代码为(组合声明/定义后):

struct Timer {
  template <typename T>
  const long getDuration() const; 
};

template <typename T>
const long Timer::getDuration() const {
   long time = static_cast<long>(std::chrono::duration_cast<T>(end - start).count());

  return time > 0 ? time : 1L;
}

现在的解释是,对于模板,编译器需要定义才能对其进行处理,但这似乎并非一直如此。

例如,对于我来说,这是与上面的代码相似的代码,但是我可以将声明放在头文件中,将定义放在源文件中:

// Enity.hpp
class Entity {
  public:
    template <typename T>
    bool hasComponent() const;
}

// Entity.cpp
template <typename T>
bool Entity::hasComponent() const {
  return componentBitSet[getComponentTypeId<T>()];
}

我对现代c ++还是比较陌生,所以我试图理解为什么在第一个示例中我需要在同一文件中进行声明和定义,而在第二个示例中将它们分开。

2 个答案:

答案 0 :(得分:3)

  

为什么有时需要在头文件中放入c ++模板函数定义?

因为有时(经常),您可能希望在多个翻译单元中实例化模板。

  

但是我可以将声明放在头文件中,并将定义放在源文件中

请记住如何将文件包含到另一个文件中。预处理器只需将包含的文件复制到源文件中即可。

因此,如果将模板定义放在头文件中并将其包含到源文件中是正确的(实际上是正确的),那么将其直接放入模板中当然也是正确的。源文件-编译器看到的内容(经过预处理)没有任何区别。

但是请记住,模板只能在已定义模板的翻译单元中实例化-这是您所作解释的结果。因此,如果您尝试:

// Other.cpp
#include "Enity.hpp"

struct X {};

void foo {
    Entity e;
    e.hasComponent<X>();
}

由于Other.cpp如果没有Enity::hasComponent<X>的定义就无法实例化Enity::hasComponent,因此无法正常工作。

答案 1 :(得分:0)

之所以需要在头文件中定义template是因为编译器会根据调用时使用的类型为template生成代码。

如果您将定义放在源文件中,则编译器将知道模板的存在,但是在发现您正在调用template时将缺少定义(理论上可以在任何地方),从而产生错误。

请注意,当您不调用template时,此错误将保持沉默,因为编译器不必为此生成代码。也许这就是为什么您在将定义与声明分开时首先没有遇到任何错误的原因?