我应该总是在头文件中定义我的整个模板类吗?

时间:2014-10-04 22:04:45

标签: c++ templates

出于某种原因,当您将模板类(即声明为template <typename T> class Thing的类)拆分为.h.cpp文件时,C ++似乎不喜欢它,就像你对其他任何课程一样。

这是否意味着在编写模板类时,我应该在头文件中全部写出来?在这些情况下,C ++程序员做了什么?

编辑:我知道在.h文件中可以有其他选择。但什么是最好的选择?或者最常见的选择是什么?

2 个答案:

答案 0 :(得分:4)

将模板的定义拆分为另一个文件很流行。它如此受欢迎甚至可能是最常见的做法,但我对此并不乐观。

认为这是不好的做法(而不是将定义放在同一个标​​题中),原因如下。

编写代码时的优先级是(最重要的):

  1. 正确性。
  2. 运行时性能。
  3. 编制时间表现。
  4. 可维护性/易于阅读。
  5. 易于写作。
  6. 只要模板定义位于相同的标题中,或者标题包含的另一个文件中,第一优先级正确性就不会受到影响。

    第二优先级运行时性能至少不会受到影响。

    将定义放在单独的文件中会对第三个优先级编译时性能产生负面影响。多少值得商榷。但是编译器无法编译X量的代码并打开/关闭两个文件,因为它可以在打开/关闭一个文件时编译完全相同的代码。我的一位优秀的编译器作者曾经告诉我,我能让他的编译器做的最慢的事情就是打开一个文件。这是在我们进行与今天一样多的编译时计算之前,但仍然......

    第四个优先级可维护性/易于阅读是非常主观的。但在我看来,单独的文件会对此优先级产生负面影响。当我在阅读代码时,如果它只在一个文件中,我通常更容易理解它。当我的名字或位置不明显时,我必须去寻找文件时,我真的很生气。当定义被拆分成许多文件时,我会更加恼火。为了我的钱,一个文件优化了这个优先级。

    第五优先级易于编写也非常主观。我不认为这方面有多大优势。将所有内容放在一个文件中会更容易一些。但是创建一个新文件肯定不难。我给出了“一档文件”这个优先级的一个非常小的优势。

    总而言之,在我的五个优先事项中,最重要的两个,这个决定绝对没有区别,而且单独的文件对3个较低的优先级产生很小的负面影响。在其中最重要的3个优先级中,编译时性能受到客观的负面影响。对于它们是否受到正面或负面影响,两个最低优先级无疑是主观的。

    我认为将模板定义放在单独的标题中没有任何好处和少量成本。

答案 1 :(得分:1)

  

“这是否意味着在编写模板类时,我应该在头文件中全部写出来?在这些情况下,C ++程序员做了什么?”

您可以做的(并且是广泛传播/流行的做法)是单独的实现代码到特殊的模板实现文件,而这些文件将被包含声明的模板头部所包含。 如果你想隐藏实现细节而不是将头文件溢出来变大,那么对于大多数情况来说,这种技术的增益被认为很少,尽管它有它的要点。

关键是不要在单独的翻译单元中使用模板定义代码或专业化,这些可以通过其他翻译单元直接看到,包括模板头文件。

常见的模式是

<强> MyTemplate.h

#if !defined(MYTEMPLATE_H_)
#define MYTEMPLATE_H_
namespace myspace {
    template <typename T> 
    class MyTemplate {
    public:
        void foo(T value);
    };

#include "MyTemplate.tcc"
}
#endif // MYTEMPLATE_H_

<强> MyTemplate.tcc

// NOTE:  There aren't header guards for this file intentionally!
template<typename T>
void MyTemplate<T>::foo(T value) {
   // ...
}

模板实施文件的其他常用扩展程序包括.icc.ipp.impl。重要的是, 它不应该是.cpp ,因为大多数IDE或构建系统框架都会将其作为翻译单元进行跟踪,除非它被明确排除({{3 }})。


  

“那么简单而不是.cpp #include标题,标题#include.tpp(包含实现)?”

模板类对here's a sample why的工作方式略有不同。提供类声明的常规头文件不应包含实现,因为当不同的翻译单元包含ODR时,将违反ODR:

<强> MyClass.h

class MyClass {
public:
    void foo(int);
};

// Violates ODR if MyClass.h is included from different translation units (.cpp files)
void MyClass::foo(int param) {
}

模板版

template<typename T>
class MyClass {
public:
    void foo(T);
};

// Doesn't violate ODR if MyClass.h is included from different translation units 
// (.cpp files), since the template parameter isn't instatiated here yet.
template<typename T>
void MyClass<T>::foo(T param) {
}

一旦一个翻译单元实例化MyClass<int>之类的东西,其他翻译单元实例化相同的模板签名,将使用第一个看到的翻译单元。

后一个实现部分可以替换为包含该代码的#include "MyClass.impl",如果您认为它过多地影响了头文件的可读性或可维护性。

作为#include "MyClass.tcc"技术的一个小缺点,你应该注意到,大多数流行的IDE都会为这些模板实现文件处理语法高亮和智能感知。