首先:将函数模板特化放在源文件中没有问题,因为 - 当特化时 - 它就像任何其他函数一样。棘手的是让来源#include带有模板定义的标头存在这种专门化。显然,在 c++ 中没有办法 declare 专业化。你可以做的是为一些T 强制实例化get_resource 模板(我们显然需要这样做,因为我们希望将特化编译成库二进制文件)。这可以使用以下语法来完成:
//force instantiation of get_resource for T=SOME_TYPE
template std::pair<const uint8_t*, size_t> get_resource<SOME_TYPE>();
但是:如果我们把它放在头文件中,就在模板定义之后,我们希望能够特化它,因为你不能特化已经实例化的东西。如果我们将它放在源文件中,在特化之后,源#includeing 带有模板定义的标头将无法看到它,因为它从未被声明为存在。这就是extern template 来救援的时刻。使用额外的extern 关键字强制实例化是一种声明,该模板函数将为某些给定的T(上面示例中的SOME_TYPE)实例化,但还没有。所以我们现在可以做的是使用这个extern关键字在头文件中声明强制实例化,然后在源文件中定义我们的特化和after来进行实际的强制实例化(没有extern)。这将确保特化对于来源#includeing 是可见的,同时它的定义可以放在源文件中(编译为单独的翻译单元)。
TL;DR
头文件
template <typename T>
std::pair<const uint8_t*, size_t> get_resource()
{
return {nullptr, 0ull};
}
extern template std::pair<const uint8_t*, size_t> get_resource<SOME_TYPE>();
源文件
template<> std::pair<const uint8_t*, size_t> get_resource<SOME_TYPE>()
{
return {reinterpret_cast<const uint8_t*>("whatever"), 9ull};
}
template std::pair<const uint8_t*, size_t> get_resource<SOME_TYPE>();