首页 > 解决方案 > 如何在源文件中专门化模板函数?

问题描述

我们在开发引擎时遇到了这个问题。我们想拥有

template <typename T>
std::pair<const uint8_t*, size_t> get_resource()
{
    return {nullptr, 0ull};
}

并专门为许多Ts。重要的部分是我们希望专业化存在于单独的源 ( .cpp) 文件中并编译到我们引擎的静态库二进制文件中。将专业化放在源文件中至关重要,因为这些功能可能会被引擎的巨大代码库(至少几十万行)的其他标头频繁更改和修改。 #include因此,如果它们存在于头文件中,则可能会在更改专业化定义之一时导致大部分项目的重建。对于这样的问题我找不到帮助,所以我在这里发布我们的解决方案。

标签: c++templatestemplate-specializationspecialization

解决方案


首先:将函数模板特化放在源文件中没有问题,因为 - 当特化时 - 它就像任何其他函数一样。棘手的是让 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关键字强制实例化是一种声明,该模板函数将为某些给定的TSOME_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>();

推荐阅读