编写模板类时,我们必须将方法主体内联到.h
文件中(除非在.cpp
文件中实例化它们(。我们知道修改内联方法需要重新编译包含它们的单元。这将使编译时间变长。实现模板类的另一种技术是在.cpp
文件中实例化它。
文件Test.h
:
template <typename T>
class Test
{
public:
T data;
void func();
};
文件Test.cpp
:
template <typename T>
void Test<T>::func()
{
}
template class Test<float>; // explicit instantiation
井。这种技术是否有效地减少了func()
修改后使用Test<float>
的编译时间?
由于成员函数的定义都在 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>
或包含标头但根本不实例化模板的翻译单元,将有一些额外的成本(解析器需要处理定义(,但不会产生代码(二进制(,或浪费在优化器和/或链接器丢弃重复符号上的时间。
正如您提到的,它还意味着,如果标头的内容发生更改,则重新编译包含该标头的所有用户代码。