First of all: there's no problem in placing function template specialization in source file since - when specialized - it's a function just like any other. What's tricky is letting sources #include
ing the header with template definition that such specialization exists. Apparently there's no way in c++ to declare specialization. What you can do is force instantiation of get_resource
template for some T
(which we obviously need to do anyway because we want specializations be compiled into library binary). This can be done with following syntax:
//force instantiation of get_resource for T=SOME_TYPE
template std::pair<const uint8_t*, size_t> get_resource<SOME_TYPE>();
But: if we place it in header file, just after template definition, we want be able to specialize it because you can't specialize something which is already instantied. If we place it in source file, just after specialization, sources #include
ing the header with template definition won't be able to see it because it was never declared to exist. That's the moment when extern template
comes to the rescue. Forced instantiation with extra extern
keyword is kind of declaration that this template function will be instantied for some given T
(SOME_TYPE
in example above), but not yet. So what we can do now is use this extern
keyword to declare forced instantiation in header file, then in source file define our specialization and after that do actual forced instantiation (without extern
). This will make sure that specialization is visible for sources #include
ing it and at the same time its definition can be placed in source file (compiled as separate translation unit).
TL;DR
Header file
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>();
Source file
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>();