Partial template specialization
Позволяет настраивать шаблоны классов и переменных (since C++14) для заданной категории шаблонных аргументов.
Содержание |
Синтаксис
template
<
список-параметров
>
ключевое-слово-класса
имя-заголовка-класса
<
список-аргументов
>
объявление
|
(1) | ||||||||
template
<
список-параметров
>
последовательность-спецификаторов-объявления
декларатор
<
список-аргументов
>
инициализатор
(необязательно)
|
(2) | (начиная с C++14) | |||||||
где class-head-name определяет имя ранее объявленного class template и declarator определяет имя ранее объявленного variable template (начиная с C++14) .
Частичная специализация может быть объявлена в любой области видимости, где может быть определена её основная шаблонная структура (что может отличаться от области видимости, где определена основная шаблонная структура; например, при внеклассовой специализации шаблона-члена ). Частичная специализация должна появляться после объявления неспециализированного шаблона.
Например,
template<class T1, class T2, int I> class A {}; // основной шаблон template<class T, int I> class A<T, T*, I> {}; // #1: частичная специализация, где 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 , который имеет частичную специализацию для массивных типов.
Список аргументов
Следующие ограничения применяются к argument-list частичной специализации шаблона:
template<class T1, class T2, int I> class B {}; // primary template template<class X, class Y, int N> class B<X, Y, N> {}; // error
|
Более того, специализация должна быть более специализированной, чем основной шаблон template<int N, typename T1, typename... Ts> struct B; template<typename... Ts> struct B<0, Ts...> {}; // Error: not more specialized |
(начиная с C++11) |
template<int I, int J> struct A {}; template<int I> struct A<I + 5, I * 2> {}; // error, I is not deducible template<int I, int J, int K> struct B {}; template<int I> struct B<I, I * 2, 2> {}; // OK: first parameter is deducible
template<class T, T t> struct C {}; // primary template template<class T> struct C<T, 1>; // error: type of the argument 1 is T, // which depends on the parameter T template<int X, int (*array_ptr)[X]> class B {}; // primary template int array[5]; template<int X> class B<X, &array> {}; // error: type of the argument &array is // int(*)[X], which depends on the parameter 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
Частичное упорядочивание
Когда шаблон класса или переменной (since C++14) инстанцируется, и доступны частичные специализации, компилятор должен решить, будет использоваться основной шаблон или одна из его частичных специализаций.
// given the template A as defined above A<int, int, 1> a1; // no specializations match, uses primary template A<int, int*, 1> a2; // uses partial specialization #1 (T = int, I = 1) A<int, char*, 5> a3; // uses partial specialization #3, (T = char) A<int, char*, 1> a4; // uses partial specialization #4, (X = int, T = char, I = 1) A<int*, int*, 2> a5; // error: matches #2 (T = int, T2 = int*, I= 2) // matches #4 (X = int*, T = int, I = 2) // neither one is more specialized than the other
Неформально «A является более специализированным, чем B» означает «A принимает подмножество типов, которые принимает B».
Формально, для установления отношения «более специализированная, чем» между частичными специализациями каждая из них сначала преобразуется в фиктивный шаблон функции следующим образом:
- первая шаблонная функция имеет те же параметры шаблона, что и первая частичная специализация, и имеет только один параметр функции, тип которого представляет собой специализацию шаблона класса со всеми аргументами шаблона из первой частичной специализации
- вторая шаблонная функция имеет те же параметры шаблона, что и вторая частичная специализация, и имеет только один параметр функции, тип которого представляет собой специализацию шаблона класса со всеми аргументами шаблона из второй частичной специализации
Шаблоны функций затем ранжируются как при function template overloading .
template<int I, int J, class T> struct X {}; // основной шаблон template<int I, int J> struct X<I, J, int> { static const int s = 1; }; // частичная специализация #1 // фиктивный шаблон функции для #1: // template<int I, int J> void f(X<I, J, int>); #A template<int I> struct X<I, I, int> { static const int 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>): дедукция успешна // #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(); // OK, использует определение члена основного шаблона a2.g(); // OK, использует определение члена частичной специализации a2.h(); // OK, использует полностью специализированное определение // члена частичной специализации 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++.
| DR | Применяется к | Поведение в опубликованной версии | Корректное поведение |
|---|---|---|---|
| CWG 727 | C++98 |
частичные и полные специализации не разрешены в
области видимости класса |
разрешены в любой области видимости |
| CWG 1315 | C++98 |
параметр шаблона не мог использоваться в константных
аргументах шаблона, кроме id-выражений |
выражения допустимы, если они выводимы |
| CWG 1495 | C++11 | спецификация была неясной при использовании пакета параметров | специализация должна быть более специализированной |
| CWG 1711 | C++14 | отсутствовала спецификация частичных специализаций шаблонов переменных | добавлена поддержка шаблонов переменных |
| CWG 1819 | C++98 | допустимые области видимости для определения частичной специализации |
разрешено объявление частичной специализации
в той же области видимости, что и основные шаблоны |
| CWG 2330 | C++14 | отсутствовали ссылки на шаблоны переменных | добавлена поддержка шаблонов переменных |