编写模板类,我们必须内联通常在.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
好。在Test<float>
上进行任何修改后,此技术是否有效减少了谁使用func()
的编译时间?
答案 0 :(得分:2)
由于成员函数的定义都在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>
或包含标题但根本不实例化模板的翻译单元,会有一些额外的成本(解析器需要处理定义)但是赢了不是生成代码(二进制),或浪费在优化器和/或链接器上的时间丢弃重复符号。
如您所述,它还意味着,如果标题的内容发生变化,则重新编译包含该标题的所有用户代码。