The C++ standard library: a tutorial and handbook 中有一句话,
The only portable way of using templates at the moment is to implement them in header files by using inline functions.(目前使用模板的唯一方法就是在头文件中实现为内联。)
为什么只能实现在头文件呢?
首先,模板也是可以不必实现在头文件中的。
我们知道 C++ 中每一个对象所占用的空间大小,是在编译的时候就确定的,在模板类没有真正的被使用之前,编译器是无法知道,模板类中使用模板类型的对象所占用的空间的大小。只有模板被真正使用的时候,编译器才知道,模板套用的是什么类型,应该分配多少空间。这也就是模板类为什么只是称之为模板,而不是泛型的缘故。
既然是在编译的时候,根据套用不同类型来进行编译,那么,套用不同类型的模板类实际上就是两个不同的类型,也就是说,stack<int>
和 stack<char>
是两个不同的数据类型,他们共同的成员函数也不是同一个函数,只不过具有相似的功能罢了。
举个例子:
template<typename T>
struct Foo
{
T bar;
void doSomething(T param)
{
....
}
};
// 在另一个 .cpp 文件中定义如下变量
Foo<int> f;
编译器需要做的就是根据这个模板创建一个新的类(姑且叫做 FooInt),其实就等价于:
struct FooInt
{
int bar;
void doSomething(int param)
{
....
}
}
当编译器需要找到方法实现,并用模板参数去实例化它们的时候,比如这个情况下是 int。如果实现不在头文件,那么方法就访问不到,自然而然编译器就不能实例化那个模板。
一个常用的方法是,在头文件中声明模板,在一个模板文件中实现具体的定义,然后在头文件的尾部包含具体实现的文件。例如:
// Foo.h
template <typename T>
struct Foo
{
void doSomething(T param);
};
#include "Foo.tpp"
// Foo.tpp
template <typename T>
void Foo<T>::doSomething(T param)
{
//implementation
}
上面的做法不够好,只是看起来是分离开了。我们可以使用下面的方法:
// Foo.h
// no implementation
template <typename T> struct Foo { ... };
// Foo.cpp
// implementation of Foo's methods
// 显示实例化,但是缺点就是,你只能使用这两个实例了,其它的都不能用
template class Foo<int>;
template class Foo<float>;
C++ Super-FAQ 也有对此说明,可以参考这里: https://isocpp.org/wiki/faq/templates#templates-defn-vs-decl