参考:
http://www.cs.technion.ac.il/users/yechiel/c+±faq/templates-defn-vs-decl.html
https://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file
如果像一般函数那样处理,简单地将模板声明、定义分别放在 .h 和 .cpp 文件中,会报错“无法解析的外部命令”。
错误原因
3个前提:
- 模板不是类或函数,模板是编译器用来生成一系列(a family of)类或函数的模式(pattern)。
- 编译器为了生成特定代码,它必须知道模板的定义、声明,以及特定的、用于“填充模板中待定类型”的具体类型。
例如,为了使用Foo<int>
,编译器必须同时“看到”Foo模板和 Foo<int>
. - 编译器在编译一个cpp文件时,不了解另一个cpp文件里的情况。
例子:
//Foo.h
template<typename T>
class Foo
{
private:
T x;
public:
Foo();
void someMethod(T x);
};
//Foo.cpp
template<typename T>
Foo<T>::Foo()
{
...
}
template<typename T>
void Foo<T>::someMethod(T x)
{
...
}
// Bar.cpp
void blah_blah_blah()
{
...
Foo<int> f;
f.someMethod(5);
...
}
- 在Foo.cpp中编译器只看到模板,不知道该用什么类型来填充“T”。
- 在 Bar.cpp中,编译器只看到它需要创建一个
Foo<int>
,但是看不到模板,不知道如何生成代码。
综上所述,编译器无法同时看到Foo模板和Foo<int>
实例,所以无法生成函数 Foo<int>::someMethod()
。
所以说,“将模板声明和定义放在一起”,实际上是为了达成 “编译器能同时“看到”生成代码的模板和用于填充模板的具体类型” 这一目的。
解决方法
- 创建一个同名的.tpp后缀文件,将模板类的实现放入其中,然后在同名.h文件末尾#include。
代码:
//Foo.tpp
template <typename T>
void Foo<T>::doSomething(T param)
{
//implementation
}
//Foo.h
template <typename T>
struct Foo
{
void doSomething(T param);
};
#include "Foo.tpp"
原文中的回答没有提到“.tpp”后缀的意义,这里我认为是为了能使文件命名一致而使用的后缀,起到的作用和.h一样。
- 在.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
这种方法为模板的使用加上了限制,只能使用 int 和 float 实现 Foo 模板类。