Прежде всего: нет проблем с размещением специализации шаблона функции в исходном файле, поскольку - когда она специализирована - это функция, как и любая другая. Хитрость заключается в том, что источники #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
). Это обеспечит видимость специализации для источников #include
, и в то же время ее определение может быть помещено в исходный файл (скомпилирован как отдельная единица перевода).
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>();