Явная (полная) специализация шаблона
Позволяет настроить код шаблона для заданного набора аргументов шаблона.
Содержание |
[править] Синтаксис
template <> объявление
|
|||||||||
Любое из следующего может быть полностью специализировано:
- шаблон функции
- шаблон класса
- (начиная с C++14) шаблон переменной
- функция-элемент шаблона класса
- статический элемент-данных шаблона класса
- класс-элемент шаблона класса
- элемент перечисление шаблона класса
- шаблон класса-элемента класса или шаблона класса
- шаблон функции-элемента класса или шаблона класса
Например,
#include <iostream> template<typename T> // первичный шаблон struct is_void : std::false_type { }; template<> // явная специализация для T = void struct is_void<void> : std::true_type { }; int main() { // для любого типа T, кроме void, // класс является производным от false_type std::cout << is_void<char>::value << '\n'; // но когда T имеет тип void, класс является // производным от true_type std::cout << is_void<void>::value << '\n'; }
[править] В деталях
Явная специализация может быть объявлена в любой области видимости, где может быть определён её первичный шаблон. (которая может отличаться от области видимости, в которой определён первичный шаблон; например, с внеклассовой специализацией шаблона элемента) . Явная специализация должна быть указана после объявления неспециализированного шаблона.
namespace N { template<class T> class X { /*...*/ }; // первичный шаблон template<> class X<int> { /*...*/ }; // специализация в том же пространстве имён template<class T> class Y { /*...*/ }; // первичный шаблон template<> class Y<double>; // предварительно объявленная специализация для double } template<> class N::Y<double> { /*...*/ }; // OK: специализация в том же пространстве имён
Специализация должна быть объявлена перед первым использованием, которое вызовет неявное создание экземпляра, в каждой единице трансляции, где происходит такое использование:
class String {}; template<class T> class Array { /*...*/ }; template<class T> void sort(Array<T>& v) { /*...*/ } // первичный шаблон void f(Array<String>& v) { sort(v); // неявно создаёт sort(Array<String>&), } // используя первичный шаблон для sort() template<> // ОШИБКА: явная специализация sort(Array<String>) void sort<String>(Array<String>& v); // после неявного создания экземпляра
Специализация шаблона, которая была объявлена, но не определена, может использоваться так же, как любой другой неполный тип (например могут использоваться указатели и ссылки на него)
template<class T> class X; // первичный шаблон template<> class X<int>; // специализация (объявлена, не определена) X<int>* p; // OK: указатель на неполный тип X<int> x; // ошибка: объект неполного типа
[править] Явные специализации шаблонов функций
При специализации шаблона функции аргументы этого шаблона могут быть опущены, если вывод аргументов шаблона может предоставить их из аргументов функции:
template<class T> class Array { /*...*/ }; template<class T> void sort(Array<T>& v); // первичный шаблон template<> void sort(Array<int>&); // специализация для T = int // не нужно писать // template<> void sort<int>(Array<int>&);
Функция с тем же именем и тем же списком аргументов, что и специализация, не является специализацией. (смотрите перегрузку шаблона в шаблонах функций)
Явная специализация шаблона функции является встроенной, только если она объявлена со спецификатором inline (или определена как удалённая), и не имеет значения, является ли первичный шаблон встроенным.
Аргументы функции по умолчанию не могут быть указаны в явных специализациях шаблонов функций, шаблонов функций-элементов и функций-элементов шаблонных классов, когда класс создаётся неявно.
Явная специализация не может быть friend объявлением.
| Этот раздел не завершён Причина: просмотреть требования к спецификации исключений в разных версиях C++ |
[править] Элементы специализаций
При определении элемента явно специализированного шаблона класса вне тела класса синтаксис template <> не используется, за исключением случаев, когда он является элементом явно специализированного шаблона класса-элемента, который специализирован как шаблон класса, потому что в противном случае синтаксис потребовал бы, чтобы такое определение начиналось с template<параметры>, требуемого вложенным шаблоном
template< typename T> struct A { struct B {}; // класс-элемент template<class U> struct C { }; // шаблон класса-элемента }; template<> // специализация struct A<int> { void f(int); // функция-элемент специализации }; // template<> не используется для элемента специализации void A<int>::f(int) { /* ... */ } template<> // специализация класса-элемента struct A<char>::B { void f(); }; // template<> не используется для элемента специализированного класса-элемента void A<char>::B::f() { /* ... */ } template<> // специализация шаблона класса-элемента template<class U> struct A<char>::C { void f(); }; // template<> используется при определении элемента явно специализированного // шаблона класса-элемента, специализированного как шаблон класса template<> template<class U> void A<char>::C<U>::f() { /* ... */ }
Явная специализация статического элемента данных шаблона, это определение, если объявление включает инициализатор; в противном случае это объявление. Эти определения должны использовать фигурные скобки для инициализации по умолчанию:
template<> X Q<int>::x; // объявление статического элемента template<> X Q<int>::x (); // ошибка: объявление функции template<> X Q<int>::x {}; // определение статического элемента, инициализированного // по умолчанию
Элемент или шаблон-элемент шаблона класса может быть явно специализирован для данного неявного создания экземпляра шаблона класса, даже если элемент или шаблон-элемент определён в определении шаблона класса.
template<typename T> struct A { void f(T); // элемент, объявленный в первичном шаблоне void h(T) {} // элемент, определённый в первичном шаблоне template<class X1> void g1(T, X1); // шаблон-элемент template<class X2> void g2(T, X2); // шаблон-элемент }; // специализация элемента template<> void A<int>::f(int); // специализация элемента ОК, даже если она определена в классе template<> void A<int>::h(int) {} // определение шаблона-элемента вне класса template<class T> template<class X1> void A<T>::g1(T, X1) { } // специализация шаблона-элемента template<> template<class X1> void A<int>::g1(int, X1); // специализация шаблона-элемента template<> template<> void A<int>::g2<char>(int, char); // для X2 = char // то же самое, используя вывод аргументов шаблона (X1 = char) template<> template<> void A<int>::g1(int, char);
Элемент или шаблон элемента могут быть вложены в несколько шаблонов включающих классов. В явной специализации для такого элемента существует template<> для каждого включающего явно специализированного шаблона класса.
template<class T1> struct A { template<class T2> struct B { template<class T3> void mf(); }; }; template<> struct A<int>; template<> template<> struct A<char>::B<double>; template<> template<> template<> void A<char>::B<char>::mf<double>();
В таком вложенном объявлении некоторые уровни могут оставаться неспециализированными (за исключением того, что они не могут специализировать шаблон-элемент класса, если его включающий класс неспециализирован). Для каждого из этих уровней объявление требует template<аргументы>, потому что такие специализации сами являются шаблонами:
template <class T1> class A { template<class T2> class B { template<class T3> void mf1(T3); // шаблон-элемент void mf2(); // нешаблонный элемент }; }; // специализация template<> // для специализированного A template<class X> // для неспециализированного B class A<int>::B { template <class T> void mf1(T); }; // специализация template<> // для специализированного A template<> // для специализированного B template<class T> // для неспециализированной mf1 void A<int>::B<double>::mf1(T t) { } // ОШИБКА: B<double> является специализированным и является шаблоном-элементом, // поэтому его включающий A также должен быть специализированным template<class Y> template<> void A<Y>::B<double>::mf2() { }
[править] Отчёты о дефектах
Следующие изменения поведения были применены с обратной силой к ранее опубликованным стандартам C++:
| Номер | Применён | Поведение в стандарте | Корректное поведение |
|---|---|---|---|
| CWG 531 | C++98 | синтаксис определения элементов явных специализаций в области пространства имён не был специфицирован |
специфицирован |
| CWG 727 | C++98 | полные специализации не разрешены в области видимости класса, даже если существуют частичные |
полная специализация разрешена в любой области видимости |
| CWG 730 | C++98 | шаблоны-элементы классов, не являющихся шаблонами, не могли быть полностью специализированными |
позволено |
| CWG 2604 | C++11 | было неясно, переносятся ли атрибуты основного шаблона в его явные специализации |
не переносятся |