由于成员函数的定义都在 cpp 内部,因此其他翻译单元无法使用,因此函数不会被隐式实例化,因此编译代码的成本仅限于单个 cpp。
这种方法的问题是您将模板的使用限制为您为其提供手动实例化的类型(或类型)。外部用户无法为其他类型实例化它,如果必须这样做,您需要记住手动专门化您要使用的每种类型。
有一种替代方法,成本稍高(不多),但它是通用的,并且比简单的方法编译起来更快。您可以在标头中提供模板定义,但指示编译器不要为一组常见类型隐式实例化它,然后在单个翻译单元中为其提供手动实例化:
// .h
#ifndef TEST_H
#define TEST_H
template <typename T>
class Test
{
public:
T data;
void func() { ... } // definition here
};
extern template class Test<float>; // Declare explicit instantiation for float
extern template class Test<int>; // for int
#endif /* TEST_H */
// cpp
#include "test.h"
template class Test<float>; // explicit instantiation
template class Test<int>;
在这种方法中,模板对于用户可能想要使用的任何类型的实例都是可见的。但是您明确告诉编译器不要为您提供特化的已知类型子集执行工作。如果用户Test<std::string>
需要,那么编译器将隐式实例化它,并且该翻译单元将付出代价。对于仅实例化的翻译单元Test<float>
,Test<int>
或包含标头但根本不实例化模板的翻译单元,将产生一些额外成本(解析器需要处理定义)但不会生成代码(二进制),或浪费在优化器和/或链接器上丢弃重复符号的时间。
正如您所提到的,如果标头的内容发生更改,它还意味着重新编译包含该标头的所有用户代码。