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

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

这是为什么?

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


当前回答

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

template < typename ... >
class MyClass
{

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

其他回答

这是完全正确的,因为编译器必须知道它的分配类型。模板类、函数、枚举等。。如果要将头文件公开或作为库的一部分(静态或动态),则也必须在头文件中实现,因为头文件不像c/cpp文件那样编译。如果编译器不知道该类型,则无法编译它。在.Net中,它可以编译,因为所有对象都派生自Object类。这不是.Net。

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


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;

    ...
}

模板通常用于标头中,因为编译器需要实例化不同版本的代码,这取决于为模板参数给定/推导的参数,并且(作为程序员)更容易让编译器多次重新编译同一代码,然后进行重复数据消除。请记住,模板并不直接表示代码,而是该代码的多个版本的模板。当您在.cpp文件中编译非模板函数时,您正在编译一个具体的函数/类。模板的情况并非如此,它可以用不同的类型实例化,也就是说,当用具体类型替换模板参数时,必须发出具体的代码。

export关键字有一个功能,用于单独编译。导出功能在C++11中被弃用,而且,AFAIK中只有一个编译器实现了它。你不应该利用出口。在C++或C++11中,单独编译是不可能的,但在C++17中,如果概念允许,我们可以有一些单独编译的方法。

为了实现单独的编译,必须可以进行单独的模板体检查。似乎可以用概念来解决问题。看看最近发表在标准委员会会议。我认为这不是唯一的要求,因为您仍然需要为用户代码中的模板代码实例化代码。

模板的单独编译问题我想也是迁移到模块时出现的问题,目前正在进行中。

编辑:截至2020年8月,模块已经成为C++的现实:https://en.cppreference.com/w/cpp/language/modules

单独实现的方法如下。

内部_内部.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或其他工具时都很烦人。

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

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

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

我采用了以下方法,这也适用于较旧的编译器(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;
}

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