非模板类中的模板函数 - H和CPP文件之间的划分

时间:2012-01-24 23:10:54

标签: c++ templates

我曾经(并且已经很长时间)认为你必须完全定义.h文件中的所有模板函数,以避免因模板编译过程而发生的多个定义错误(非C ++ 11) )。

我正在读一个同事的代码,他有一个非模板类,其中有一个模板函数,并且他将函数声明与函数定义(在H中声明,在CPP中定义)分开。它令我惊讶地编译并正常工作。

非模板类中的模板函数如何编译,以及如何编译模板类中的函数之间是否存在差异?有人可以解释这种差异或者我可能会感到困惑的地方吗?

2 个答案:

答案 0 :(得分:4)

有趣的是模板实例化的方式和时间。如果可以在链接时找到实例化,则不需要在头文件中显示模板 definition

有时,显式实例化是这样的原因:

  • 标题:

    struct X { 
        // function template _declaration_
        template <typename T> void test(const T&);
    };
    
  • CPP:

    #include "X.h"
    
    // function template _definition_:
    template <typename T> 
        void X::test(const T&)
    {
    }
    
    // explicit function template _instantiation(s)_:
    template X::test<int>(const int&);    
    template X::test<std::string>(const std::string&);
    

使用此示例,除非在其他翻译单元中使用模板的未实例化定义,否则链接将成功

答案 1 :(得分:3)

在命名空间或类范围中定义的函数模板之间没有区别。是否在类模板内也无关紧要。重要的是,在项目的某个时刻,任何使用过的函数模板(无论是成员还是非成员)都会被实例化。让我们回顾一下不同的情况:

  1. 不需要实例化未使用的函数模板,因此在任何时间点编译器都不需要它们的实现。这听起来很无聊但很重要,例如当使用SFINAE方法时,声明类或函数模板但未定义。
  2. 任何在使用它时定义的函数模板都将由编译器以允许跨不同转换单元的多个定义的形式实例化:仅保留一个实例化。重要的是要合并所有不同的定义,因为如果你在函数模板中使用了函数模板的地址或者使用了一个状态变量,你可以检测到差异:每个实例都只有一个。
  3. 最有趣的设置是在使用函数模板时,函数模板的定义是而不是:在这种情况下,编译器无法实例化它。当编译器在不同的转换单元中看到函数模板的定义时,它将不知道要实例化哪个模板参数!一个Catch 22?好吧,你总是可以显式地实例化一次模板。具有多个显式实例化将创建多个定义的符号。
  4. 这些大致是重要的选择。通常有很好的理由不希望在标题中定义函数模板。例如,您不一定要拖动您不会拥有的依赖项。将函数模板的定义放在其他地方并明确地实例化它是一件好事。此外,您可能希望减少编译时间,例如通过避免使用流在每个转换单元中实质上实例化整个I / O流和语言环境库。在C ++ 2011中引入了 extern templates ,它允许声明特定模板(函数或类模板)在外部为整个程序定义一次,并且不需要在每个头中实例化它使用特别常见的模板参数。

    对于我刚才所说的更长版本,包括示例,请查看blog post 我上周末写了这个话题。