Частичная специализация шаблона
Позволяет настраивать шаблоны классов и переменных(начиная с C++14) для заданной категории аргументов шаблона.
Содержание |
[править]Синтаксис
template < список-параметров> ключевое-слово-classимя-заголовка-класса< список-аргументов> объявление | (1) | ||||||||
template < список-параметров> последовательность-спецификаторов-объявлениядекларатор< список-аргументов> инициализатор (необязательно) | (2) | (начиная с C++14) | |||||||
где имя-заголовка-класса определяет имя ранее объявленного шаблона класса и декларатор определяет имя ранее объявленного шаблона переменной(начиная с C++14).
Частичная специализация может быть объявлена в любой области видимости, где может быть определён её основной шаблон (которая может отличаться от области видимости, в которой определён основной шаблон; например, при внеклассовой специализации шаблона элемента). Частичная специализация должна появиться после объявления неспециализированного шаблона.
Например,
template<class T1, class T2, int I>class A {};// основной шаблон template<class T, int I>class A<T, T*, I>{};// частичная специализация, где T2 это указатель на T1 template<class T, class T2, int I>class A<T*, T2, I>{};// #2: частичная специализация, где T1 это указатель template<class T>class A<int, T*, 5>{};// #3: частичная специализация, где// T1 это int, I равно 5 и T2 это указатель template<class X, class T, int I>class A<X, T*, I>{};// #4: частичная специализация, где T2 это указатель
Примеры частичной специализации в стандартной библиотеке включают std::unique_ptr, который имеет частичную специализацию для типов массивов.
[править]Список аргументов
К списку-аргументов частичной специализации шаблона применяются следующие ограничения:
template<class T1, class T2, int I>class B {};// основной шаблонtemplate<class X, class Y, int N>class B<X, Y, N>{};// ошибка
Более того, специализация должна быть более специализированной, чем основной шаблон. template<int N, typename T1, typename... Ts>struct B;template<typename... Ts>struct B<0, Ts...>{};// Ошибка: не более специализированный | (начиная с C++11) |
template<int I, int J>struct A {};template<int I>struct A<I+5, I*2>{};// ошибка, I не выводим template<int I, int J, int K>struct B {};template<int I>struct B<I, I*2, 2>{};// OK: первый параметр выводим
template<class T, T t>struct C {};// основной шаблонtemplate<class T>struct C<T, 1>;// ошибка: тип аргумента 1 это T,// который зависит от параметра T template<int X, int(*array_ptr)[X]>class B {};// основной шаблонint array[5];template<int X>class B<X, &array>{};// ошибка: тип аргумента &array это// int(*)[X], который зависит от параметра X
[править]Поиск имени
Частичные специализации шаблонов не могут быть найдены при поиске по имени. Частичные специализации учитываются, только если первичный шаблон найден по имени. В частности, объявление using, которое делает видимым первичный шаблон, также делает видимыми частичные специализации:
namespace N {template<class T1, class T2>class Z {};// основной шаблон}using N::Z;// ссылается на основной шаблон namespace N {template<class T>class Z<T, T*>{};// частичная специализация} Z<int, int*> z;// поиск имени находит N::Z (основной шаблон),// затем используется частичная специализация с T = int
[править]Частичное упорядочивание
Когда создаётся экземпляр шаблона класса или переменной(начиная с C++14) и доступны частичные специализации, компилятор должен решить, будет ли использоваться основной шаблон или одна из его частичных специализаций.
// учитывая шаблон A, как определено выше A<int, int, 1> a1;// нет совпадений специализаций, используется основной шаблон A<int, int*, 1> a2;// используется частичная специализация #1 (T=int, I=1) A<int, char*, 5> a3;// используется частичная специализация #3, (T=char) A<int, char*, 1> a4;// используется частичная специализация #4, (X=int, T=char, I=1) A<int*, int*, 2> a5;// ошибка: соответствует #2 (T=int, T2=int*, I=2)// соответствует #4 (X=int*, T=int, I=2)// ни одна из них не является более специализированной, чем другая
Неформально "A более специализирована, чем B" означает "A принимает подмножество типов, которые принимает B".
Формально, чтобы установить более специализированные отношения между частичными специализациями, каждая из них сначала преобразуется в фиктивный шаблон функции следующим образом:
- первый шаблон функции имеет те же параметры шаблона, что и первая частичная специализация, и имеет только один параметр функции, тип которого является специализацией шаблона класса со всеми аргументами шаблона из первой частичной специализации
- второй шаблон функции имеет те же параметры шаблона, что и вторая частичная специализация, и имеет только один параметр функции, тип которого является специализацией шаблона класса со всеми аргументами шаблона из второй частичной специализации.
Затем шаблоны функций ранжируются, как будто для перегрузки шаблона функции.
template<int I, int J, class T>struct X {};// основной шаблонtemplate<int I, int J>struct X<I, J, int>{staticconstint s =1;};// частичная специализация #1// фиктивный шаблон функции для #1 это// template<int I, int J> void f(X<I, J, int>); #A template<int I>struct X<I, I, int>{staticconstint s =2;};// частичная специализация #2// фиктивный шаблон функции для #2 это// template<int I> void f(X<I, I, int>); #B int main(){ X<2, 2, int> x;// и #1 и #2 соответствуют// частичному порядку для шаблонов функций:// #A из #B: void(X<I,J,int>) из void(X<U1, U1, int>): вывод OK// #B из #A: void(X<I,I,int>) из void(X<U1, U2, int>): вывод не удался// #B более специализирована// #2 это конкретизированная специализацияstd::cout<< x.s<<'\n';// печатает 2}
[править]Элементы частичной специализации
Список параметров шаблона и список аргументов шаблона элемента частичной специализации должны соответствовать списку параметров и списку аргументов частичной специализации.
Как и в случае с элементами первичных шаблонов, их нужно определять только в том случае, если они используются в программе.
Элементы частичных специализаций не связаны с элементами основного шаблона.
Явная (полная) специализация элемента частичной специализации объявляется так же, как и явная специализация первичного шаблона.
template<class T, int I>// основной шаблонstruct A {void f();// объявление элемента}; template<class T, int I>void A<T, I>::f(){}// определение элемента основного шаблона // частичная специализацияtemplate<class T>struct A<T, 2>{void f();void g();void h();}; // элемент частичной специализацииtemplate<class T>void A<T, 2>::g(){} // явная (полная) специализация элемента// частичной специализацииtemplate<>void A<char, 2>::h(){} int main(){ A<char, 0> a0; A<char, 2> a2; a0.f();// ОК, использует определение элемента основного шаблона a2.g();// ОК, использует определение элемента частичной специализации a2.h();// ОК, использует полностью специализированное// определение элемента частичной специализации a2.f();// ошибка: нет определения f() в частичной// специализации A<T,2> (основной шаблон не используется)}
Если первичный шаблон является элементом другого шаблонного класса, его частичные специализации являются элементами объемлющего шаблонного класса. Если создаётся экземпляр объемлющего шаблона, также создаётся экземпляр объявления каждой частичной специализации элемента (так же, как создаются объявления, но не определения, всех других элементов шаблона).
Если первичный шаблон элемента явно (полностью) специализирован для данной (неявной) специализации шаблона объемлющего класса, частичные специализации шаблонного элемента игнорируются для этой специализации шаблона объемлющего класса.
Если частичная специализация шаблонного элемента явно специализирована для данной (неявной) специализации шаблона объемлющего класса, основной шаблонный элемент и другие его частичные специализации по-прежнему рассматриваются для этой специализации шаблона объемлющего класса.
template<class T>struct A // объемлющий шаблон класса{template<class T2>struct B {};// основной шаблон элементаtemplate<class T2>struct B<T2*>{};// частичная специализация шаблона элемента}; template<>template<class T2>struct A<short>::B{};// полная специализация основного шаблонного// элемента (будет игнорироваться частичная) A<char>::B<int*> abcip;// использует частичную специализацию T2=int A<short>::B<int*> absip;// использует полную специализацию основного// шаблона (игнорирует частичную) A<char>::B<int> abci;// использует основной шаблон
[править]Отчёты о дефектах
Следующие изменения поведения были применены с обратной силой к ранее опубликованным стандартам C++:
Номер | Применён | Поведение в стандарте | Корректное поведение |
---|---|---|---|
CWG 727 | C++98 | частичная и полная специализация не разрешена в области видимости класса | разрешена в любом области видимости |
CWG 1315 | C++98 | параметр шаблона нельзя было использовать в аргументах шаблона, отличных от типа, кроме выражений идентификаторов | выражения правильно, пока выводится |
CWG 1495 | C++11 | спецификация была неясна при использовании пакета параметров | специализация должна быть более специализированной |
CWG 1711 | C++14 | отсутствует спецификация частичных специализаций шаблона переменной | добавлена поддержка переменных шаблонов |
CWG 1819 | C++98 | допустимые области видимости определения частичной специализации | астичная специализация может быть объявлена в той же области, что и основные шаблоны |
CWG 2330 | C++14 | отсутствуют ссылки на шаблоны переменных | добавлена поддержка переменных шаблонов |