c++ - 如何在源文件中专门化模板函数?
问题描述
我们在开发引擎时遇到了这个问题。我们想拥有
template <typename T>
std::pair<const uint8_t*, size_t> get_resource()
{
return {nullptr, 0ull};
}
并专门为许多T
s。重要的部分是我们希望专业化存在于单独的源 ( .cpp
) 文件中并编译到我们引擎的静态库二进制文件中。将专业化放在源文件中至关重要,因为这些功能可能会被引擎的巨大代码库(至少几十万行)的其他标头频繁更改和修改。 #include
因此,如果它们存在于头文件中,则可能会在更改专业化定义之一时导致大部分项目的重建。对于这样的问题我找不到帮助,所以我在这里发布我们的解决方案。
解决方案
首先:将函数模板特化放在源文件中没有问题,因为 - 当特化时 - 它就像任何其他函数一样。棘手的是让 sources#include
用模板定义标头,这样的专业化存在。显然,在 c++ 中没有办法声明专业化。您可以做的是强制实例化get_resource
某些模板T
(我们显然需要这样做,因为我们希望将特化编译成库二进制文件)。这可以使用以下语法来完成:
//force instantiation of get_resource for T=SOME_TYPE
template std::pair<const uint8_t*, size_t> get_resource<SOME_TYPE>();
但是:如果我们把它放在头文件中,在模板定义之后,我们希望能够特化它,因为你不能特化已经实例化的东西。如果我们将它放在源文件中,在特化之后,#include
带有模板定义的头文件将无法看到它,因为它从未被声明为存在。extern template
这就是救援的时刻。使用额外extern
关键字强制实例化是一种声明,该模板函数将为某些给定的T
(SOME_TYPE
在上面的示例中)实例化,但还没有。所以我们现在可以做的是使用这个extern
关键字在头文件中声明强制实例化,然后在源文件中定义我们的特化,然后进行实际的强制实例化(没有extern
)。这将确保专业化对于 source 是可见的,#include
同时它的定义可以放在源文件中(编译为单独的翻译单元)。
TL;博士
头文件
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>();