【问题标题】:Why do C++ template definitions need to be in the header? [duplicate]为什么 C++ 模板定义需要在标头中? [复制]
【发布时间】:2011-03-03 11:52:16
【问题描述】:

可能重复:
Why should the implementation and the declaration of a template class be in the same header file?

例如,在定义模板类时,为什么类方法的实现需要在标头中?为什么它们不能在实现文件中(cpp/cxx)?

【问题讨论】:

  • 这是我不知道哪个问题的完全重复 :)
  • 我也找不到这样的问题...
  • 他们可以,只是你通常不希望他们这样。

标签: c++ templates


【解决方案1】:

模板类不是类,它是一个可以用来创建类的模板。当您实例化这样的类时,例如MyTemplate<int>,编译器当场创建类。为了创建它,它必须查看所有模板化的成员函数(以便它可以使用模板来创建实际的成员函数,例如 MyTemplate<int>::foo() ),因此这些模板化的成员函数必须在标题中。

如果成员不在标头中,编译器将简单地假设它们存在于其他位置,并从模板化函数声明创建实际的函数声明,这会给您带来链接器错误。

“export”关键字应该可以解决这个问题,但很少有编译器支持它(我只知道 Comeau)。

您还可以显式实例化MyTemplate<int> - 然后编译器将在编译包含MyTemplate 成员函数定义模板的cpp 文件时为MyTemplate<int> 创建实际成员函数。

【讨论】:

  • “模板类不是类”——这就是为什么我们应该称它为“类模板”,而不是问题中的“模板类”。
  • export 关键字仅由供应商支持(我认为它有一些怪癖)。同一家供应商推动将其从即将发布的标准中删除,这就像说它在该语言中不存在。
  • 如果你明确声明类型,你可以在cpp文件中拥有它们吗?
【解决方案2】:

它们需要在实例化时对编译器可见。这基本上意味着,如果您在标头中发布模板,则如果您依赖隐式实例化,则定义必须对包含该标头的所有翻译单元可见。

如果您要显式实例化模板,则无需在标头中定义它们,但在大多数情况下这不是一个好主意。

至于原因,基本上归结为编译器在解析定义时没有编译模板,而是在实例化时编译,然后针对特定的实例化类型进行编译。

【讨论】:

  • @EricSchaefer:我已经编辑了答案:原因是模板在解析时没有编译,而是在实例化时编译。
【解决方案3】:

如果您的编译器支持export,则不支持。只有基于 EDG 的编译器支持 export,因此它将从 C++0x 中删除。

非导出模板要求编译器可以看到完整的模板定义,以便为您作为参数提供的特定类型实例化它。例如:

template<typename T> 
struct X {
    T t;
    X(int i): t(i) {}
};

现在,当您在某个翻译单元中编写 X&lt;float&gt;(5) 时,编译器作为编译该翻译单元的一部分必须检查 X 的构造函数是否类型正确,为其生成代码,等等。因此它必须看到X的定义,才能允许X&lt;float&gt;(5)而禁止X&lt;char*&gt;(5)

确保编译器在所有使用它的翻译单元中看到相同的模板定义的唯一明智方法是将定义放在头文件中。不过,就标准而言,欢迎您手动复制并粘贴它,或在 cpp 文件中定义一个模板,该模板仅用于该翻译单元。

export 实际上告诉编译器它必须将模板定义的解析形式输出到一种特殊类型的目标文件中。然后链接器执行模板实例化。使用普通的工具链,编译器足够聪明,可以执行模板实例化,而链接器则不行。请记住,除了基本解析之外,模板实例化必须完成编译器所做的几乎所有所有操作

【讨论】:

    【解决方案4】:

    它们可以在 CPP 文件中。

    问题源于编译器在每个翻译单元的基础上为模板类的特定实例化构建代码(例如std::vector)。在 CPP 文件中定义函数的问题在于,您需要在该 CPP 文件中定义所有可能的形式(这称为模板特化)。

    因此,对于上面示例的 int 向量,您可以使用专门化在 CPP 文件中为 int 案例定义一个函数。

    例如

    template<> void std::vector< int >::push_back( int& intVal )
    

    当然,这样做可以产生针对特定情况进行优化的优势,但它确实让您了解 STL 可以引入多少代码膨胀!至少所有函数都没有像某个编译器那样被定义为内联;)

    【讨论】:

      【解决方案5】:

      模板的这方面称为编译模型,不要与How does C++ link template instances 的主题的实例化机制相混淆。

      实例化机制是“什么时候生成实例化?”问题的答案,实例化模型是“哪里找到源?”的答案

      有两种标准编译模型:

      • 包含,你知道的,定义必须可用的地方,

      • 分隔,允许在关键字export 的帮助下将定义放在其他地方。该标准已从标准中删除,并且在 C++0X 中不可用。删除的原因之一是它没有被广泛实施(只有一种实施)。

      有关更多信息,请参阅 David Vandevoorde 和 Nicolai Josuttis 的 C++ 模板、完整指南或 http://www.bourguet.org/v2/cpplang/export.pdf,分离编译模型是后续论文的主题。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-04-28
        • 2011-08-09
        • 1970-01-01
        • 2014-11-28
        • 2017-12-09
        相关资源
        最近更新 更多