Я только что прочитал о явной реализации шаблона:
template struct MyStruct<long>;
Он был описан как «довольно редкий», поэтому при каких обстоятельствах это будет полезно?
Одним из вариантов использования является скрытие определений от конечного пользователя.
tpl.h:
template<typename T>
void func(); // Declaration
tpl.cpp:
template<typename T>
void func()
{
// Definition
}
template void func<int>(); // explicit instantiation for int
template void func<double>(); // explicit instantiation for double
main.cpp
#include "tpl.h"int main()
{
func<double>(); // OK
func<int>(); // OK
// func<char>(); - Linking ERROR
}
Явное создание экземпляров предназначено для оптимизации использования библиотек шаблонов, предоставляя некоторые (в основном используемые) экземпляры шаблонов в скомпилированной двоичной форме вместо формы исходного кода. Это сократит время компиляции и соединения для приложений конечного пользователя. Например. std::basic_string<char>
а также std::basic_string<wchar_t>
может быть явно создан в дистрибутиве STL, избегая работы над его созданием в каждой единице перевода.
Явная реализация также полезна, когда вы хотите инкапсулировать реализацию шаблона и хотите, чтобы этот шаблон использовался только с хорошо известным набором типов. В этом случае в заголовочный файл можно поместить только объявления функций шаблона (бесплатные или члены) (.h/.hpp
) и определите их в переводческой единице (.cpp
).
Пример:
// numeric_vector.h
//////////////////////////////////////////////////
template <typename T> class numeric_vector
{
...
void sort();
};// numeric_vector.cpp
//////////////////////////////////////////////////
// We know that it shall be used with doubles and ints only,
// so we explicitly instantiate it for doubles and ints
template class numeric_vector<int>;
template class numeric_vector<double>;
// Note that you could instantiate only specific
// members you need (functions and static data), not entire class:
template void numeric_vector<float>::sort();
template <typename T> void numeric_vector<T>::sort()
{
// Implementation
...
}
Также явное создание экземпляров может быть полезно, когда вам нужен экземплярный тип из шаблона, но внутри некоторой синтаксической конструкции, которая не запускает саму реализацию, например некоторые специфичные для компилятора мета-функции, такие как __declspec(uuid)
в Visual Studio.
Обратите внимание на разницу с другим методом, который можно использовать для инкапсуляции реализации — явная специализация. При явной специализации вы должны предоставить конкретное определение для каждого типа, который будет специализированным. С явной реализацией у вас есть одно определение шаблона.
Рассмотрим тот же пример с явной специализацией:
Пример:
// numeric_vector.h
//////////////////////////////////////////////////
template <typename T> class numeric_vector
{
...
void sort();
};
template <> class numeric_vector<int>
{
...
void sort();
};
template <> class numeric_vector<double>
{
...
void sort();
};
// Specializing separate members is also allowed
template <> void numeric_vector<float>::sort();
// numeric_vector.cpp
//////////////////////////////////////////////////
void numeric_vector<int>::sort()
{
// Implementation for int
...
}
void numeric_vector<double>::sort()
{
// Implementation for double
...
}
void numeric_vector<float>::sort()
{
// Implementation for float
...
}
Наличие явной специализации позволяет скрыть реализацию, которая, как вы знаете, обычно невозможна для шаблонов.
Я видел эту технику только один раз в библиотеке, которая обрабатывает геометрию, и они предоставили свой собственный векторный класс.
Так что вы могли бы использовать
lib::Vector<MyShape>
с некоторыми основными функциями, которые lib::Vector
при условии, и основные реализации, и если вы использовали его со своими классами (некоторые, не все)
lib::Vector<lib::Polygon>
Вы бы использовали явную специализацию. У вас не будет доступа к реализации, но я держу пари, что за кулисами там происходили некоторые жесткие оптимизации.
Если вам действительно не нравится определение шаблонных функций в заголовочных файлах, вы можете определить функции в отдельном исходном файле и использовать явное создание экземпляров шаблона для создания экземпляров всех версий, которые вы используете. Тогда вам нужно только предварительные объявления в вашем заголовочном файле вместо полного определения.