引用C++标准库:教程和手册:

目前使用模板的唯一可移植方式是使用内联函数在头文件中实现它们。

这是为什么?

(澄清:头文件不是唯一的可移植解决方案。但它们是最方便的可移植方案。)


当前回答

尽管标准C++没有这样的要求,但一些编译器要求所有函数和类模板都必须在使用的每个翻译单元中可用。实际上,对于这些编译器,模板函数的主体必须在头文件中可用。重复:这意味着这些编译器不允许在.cpp文件等非头文件中定义它们

有一个导出关键字可以缓解这个问题,但它离可移植性还很远。

其他回答

尽管标准C++没有这样的要求,但一些编译器要求所有函数和类模板都必须在使用的每个翻译单元中可用。实际上,对于这些编译器,模板函数的主体必须在头文件中可用。重复:这意味着这些编译器不允许在.cpp文件等非头文件中定义它们

有一个导出关键字可以缓解这个问题,但它离可移植性还很远。

在实际将模板编译成目标代码之前,需要由编译器实例化模板。只有当模板参数已知时,才能实现此实例化。现在想象一个场景,其中模板函数在a.h中声明,在a.pp中定义,并在b.cpp中使用。编译a.pp时,不一定知道即将到来的编译b.cpp将需要模板的实例,更不用说是哪个特定的实例。对于更多的头文件和源文件,情况可能会很快变得更加复杂。

有人会说,编译器可以更聪明地“前瞻”模板的所有用途,但我确信创建递归或其他复杂场景并不困难。顺便说一句,编译器不会再这样做了。正如Anton所指出的,一些编译器支持模板实例化的显式导出声明,但并非所有编译器都支持它(现在?)。

我建议看看这个gcc页面,它讨论了模板实例化的“cfront”和“borland”模型之间的权衡。

https://gcc.gnu.org/onlinedocs/gcc-4.6.4/gcc/Template-Instantiation.html

“borland”模型符合作者的建议,提供完整的模板定义,并多次编译。

它包含关于使用手动和自动模板实例化的明确建议。例如,“-repo”选项可用于收集需要实例化的模板。或者另一个选项是使用“-fno隐式模板”禁用自动模板实例化,以强制手动模板实例化。

根据我的经验,我依赖于为每个编译单元实例化的C++标准库和Boost模板(使用模板库)。对于我的大型模板类,我为所需的类型进行了一次手动模板实例化。

这是我的方法,因为我提供的是一个工作程序,而不是用于其他程序的模板库。这本书的作者Josuttis在模板库方面做了大量工作。

如果我真的担心速度,我想我会探索使用预编译头https://gcc.gnu.org/onlinedocs/gcc/Precompiled-Headers.html

这在许多编译器中得到了支持。然而,我认为使用模板头文件时,预编译头文件会很困难。

只是在这里添加一些值得注意的内容。当模板类的方法不是函数模板时,可以在实现文件中定义它们。


myQueue.hpp:

template <class T> 
class QueueA {
    int size;
    ...
public:
    template <class T> T dequeue() {
       // implementation here
    }

    bool isEmpty();

    ...
}    

myQueue.cpp:

// implementation of regular methods goes like this:
template <class T> bool QueueA<T>::isEmpty() {
    return this->size == 0;
}


main()
{
    QueueA<char> Q;

    ...
}

单独实现的方法如下。

内部_内部.h

template <typename T>
struct Foo
{
    void doSomething(T param);
};

foo.tpp文件

#include "inner_foo.h"

template <typename T>
void Foo<T>::doSomething(T param)
{
    //implementation
}

小时

#include <foo.tpp>

主.cpp

#include <foo.h>

inner_fo.h具有前向声明。foo.tpp具有实现并包括inner_fo.h;而foo.h只有一行,包括foo.tpp。

编译时,将foo.h的内容复制到foo.tpp,然后将整个文件复制到foo.h,然后进行编译。这样,没有任何限制,并且命名是一致的,以换取一个额外的文件。

我这样做是因为代码的静态分析器在*.tpp中看不到类的前向声明时会中断。这在任何IDE中编写代码或使用YouCompleteMe或其他工具时都很烦人。