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

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

这是为什么?

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


当前回答

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


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;

    ...
}

其他回答

注意:没有必要将实现放在头文件中,请参阅答案末尾的替代解决方案。

无论如何,代码失败的原因是,在实例化模板时,编译器使用给定的模板参数创建一个新类。例如:

template<typename T>
struct Foo
{
    T bar;
    void doSomething(T param) {/* do stuff using T */}
};

// somewhere in a .cpp
Foo<int> f; 

当读取这一行时,编译器将创建一个新类(让我们称之为FooInt),这相当于以下内容:

struct FooInt
{
    int bar;
    void doSomething(int param) {/* do stuff using int */}
}

因此,编译器需要访问方法的实现,以使用模板参数(在本例中为int)实例化它们。如果这些实现不在标头中,那么它们将无法访问,因此编译器将无法实例化模板。

对此的常见解决方案是在头文件中写入模板声明,然后在实现文件(例如.tpp)中实现类,并在头的末尾包含该实现文件。

食品h

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

#include "Foo.tpp"

食品.tpp

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

这样,实现仍然与声明分离,但编译器可以访问。

替代解决方案

另一个解决方案是保持实现分离,并显式实例化您需要的所有模板实例:

食品h

// no implementation
template <typename T> struct Foo { ... };

食品.cpp

// implementation of Foo's methods

// explicit instantiations
template class Foo<int>;
template class Foo<float>;
// You will only be able to use Foo with int or float

如果我的解释不够清楚,你可以看看这个主题的C++超级常见问题解答。

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


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;

    ...
}

这意味着定义模板类的方法实现的最可移植的方式是在模板类定义中定义它们。

template < typename ... >
class MyClass
{

    int myMethod()
    {
       // Not just declaration. Add method implementation here
    }
};

我必须写一个模板类,这个例子对我很有用

下面是一个动态数组类的示例。

#ifndef dynarray_h
#define dynarray_h

#include <iostream>

template <class T>
class DynArray{
    int capacity_;
    int size_;
    T* data;
public:
    explicit DynArray(int size = 0, int capacity=2);
    DynArray(const DynArray& d1);
    ~DynArray();
    T& operator[]( const int index);
    void operator=(const DynArray<T>& d1);
    int size();
    
    int capacity();
    void clear();
    
    void push_back(int n);
    
    void pop_back();
    T& at(const int n);
    T& back();
    T& front();
};

#include "dynarray.template" // this is how you get the header file

#endif

现在,在.template文件中,您可以像往常一样定义函数。

template <class T>
DynArray<T>::DynArray(int size, int capacity){
    if (capacity >= size){
        this->size_ = size;
        this->capacity_ = capacity;
        data = new T[capacity];
    }
    //    for (int i = 0; i < size; ++i) {
    //        data[i] = 0;
    //    }
}

template <class T>
DynArray<T>::DynArray(const DynArray& d1){
    //clear();
    //delete [] data;
    std::cout << "copy" << std::endl;
    this->size_ = d1.size_;
    this->capacity_ = d1.capacity_;
    data = new T[capacity()];
    for(int i = 0; i < size(); ++i){
        data[i] = d1.data[i];
    }
}

template <class T>
DynArray<T>::~DynArray(){
    delete [] data;
}

template <class T>
T& DynArray<T>::operator[]( const int index){
    return at(index);
}

template <class T>
void DynArray<T>::operator=(const DynArray<T>& d1){
    if (this->size() > 0) {
        clear();
    }
    std::cout << "assign" << std::endl;
    this->size_ = d1.size_;
    this->capacity_ = d1.capacity_;
    data = new T[capacity()];
    for(int i = 0; i < size(); ++i){
        data[i] = d1.data[i];
    }
    
    //delete [] d1.data;
}

template <class T>
int DynArray<T>::size(){
    return size_;
}

template <class T>
int DynArray<T>::capacity(){
    return capacity_;
}

template <class T>
void DynArray<T>::clear(){
    for( int i = 0; i < size(); ++i){
        data[i] = 0;
    }
    size_ = 0;
    capacity_ = 2;
}

template <class T>
void DynArray<T>::push_back(int n){
    if (size() >= capacity()) {
        std::cout << "grow" << std::endl;
        //redo the array
        T* copy = new T[capacity_ + 40];
        for (int i = 0; i < size(); ++i) {
            copy[i] = data[i];
        }
        
        delete [] data;
        data = new T[ capacity_ * 2];
        for (int i = 0; i < capacity() * 2; ++i) {
            data[i] = copy[i];
        }
        delete [] copy;
        capacity_ *= 2;
    }
    data[size()] = n;
    ++size_;
}

template <class T>
void DynArray<T>::pop_back(){
    data[size()-1] = 0;
    --size_;
}

template <class T>
T& DynArray<T>::at(const int n){
    if (n >= size()) {
        throw std::runtime_error("invalid index");
    }
    return data[n];
}

template <class T>
T& DynArray<T>::back(){
    if (size() == 0) {
        throw std::runtime_error("vector is empty");
    }
    return data[size()-1];
}

template <class T>
T& DynArray<T>::front(){
    if (size() == 0) {
        throw std::runtime_error("vector is empty");
    }
    return data[0];
    }

尽管上面有很多很好的解释,但我缺少一种将模板分离为页眉和正文的实用方法。

我主要担心的是,当我更改其定义时,避免重新编译所有模板用户。

对我来说,在模板主体中包含所有模板实例不是一个可行的解决方案,因为模板作者可能不知道其使用情况,模板用户可能无权修改它。

我采用了以下方法,这也适用于较旧的编译器(gcc 4.3.4,aCC A.03.13)。

对于每个模板的使用,在其自己的头文件(从UML模型生成)中都有一个typedef。它的主体包含实例化(最终在一个库中,该库在末尾链接)。

模板的每个用户都包含该头文件并使用typedef。

示意图示例:

MyTemplate.h:

#ifndef MyTemplate_h
#define MyTemplate_h 1

template <class T>
class MyTemplate
{
public:
  MyTemplate(const T& rt);
  void dump();
  T t;
};

#endif

我的模板.cpp:

#include "MyTemplate.h"
#include <iostream>

template <class T>
MyTemplate<T>::MyTemplate(const T& rt)
: t(rt)
{
}

template <class T>
void MyTemplate<T>::dump()
{
  cerr << t << endl;
}

MyInstantiatedTemplate.h:

#ifndef MyInstantiatedTemplate_h
#define MyInstantiatedTemplate_h 1
#include "MyTemplate.h"

typedef MyTemplate< int > MyInstantiatedTemplate;

#endif

我的实例化模板.cpp:

#include "MyTemplate.cpp"

template class MyTemplate< int >;

main.cpp:

#include "MyInstantiatedTemplate.h"

int main()
{
  MyInstantiatedTemplate m(100);
  m.dump();
  return 0;
}

这样,只需要重新编译模板实例,而不是所有模板用户(和依赖项)。