Частичная специализация шаблонов
Позволяет настраивать шаблоны классов и переменных(с C++14) для заданной категории аргументов шаблона.
Синтаксис
template < список параметров > ключ класса имя заголовка класса < список аргументов > объявление |
(1) | |
template < список параметров > последовательность спецификаторов объявлений декларатор < список аргументов > инициализатор (необязательно) |
(2) | (с C++14) |
где имя заголовка класса идентифицирует имя ранее объявленного шаблона класса и декларатор идентифицирует имя ранее объявленного шаблона переменной(с C++14).
Частичная специализация может быть объявлена в любом области видимости, где может быть определен ее основной шаблон (что может отличаться от области видимости определения основного шаблона; например, при внеклассной специализации шаблона члена). Частичная специализация должна появиться после объявления неспециализированного шаблона.
Например,
template<class T1, class T2, int I>
class A {}; // primary template
template<class T, int I>
class A<T, T*, I> {}; // #1: partial specialization where T2 is a pointer to T1
template<class T, class T2, int I>
class A<T*, T2, I> {}; // #2: partial specialization where T1 is a pointer
template<class T>
class A<int, T*, 5> {}; // #3: partial specialization where
// T1 is int, I is 5, and T2 is a pointer
template<class X, class T, int I>
class A<X, T*, I> {}; // #4: partial specialization where T2 is a pointerПримеры частичных специализаций в стандартной библиотеке включают std::unique_ptr, которая имеет частичную специализацию для типов массивов.
Список аргументов
Следующие ограничения применяются к списку аргументов частичной специализации шаблона:
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 deducibletemplate<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 {}; // primary template
}
using N::Z; // refers to the primary template
namespace N
{
template<class T> class Z<T, T*> {}; // partial specialization
}
Z<int, int*> z; // name lookup finds N::Z (the primary template), the
// partial specialization with T = int is then usedЧастичный порядок
Когда шаблон класса или переменной(с 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».
Формально, чтобы установить отношение «более специализирован, чем» между частичными специализациями, сначала каждая из них преобразуется в фиктивный шаблон функции следующим образом:
- первый шаблон функции имеет те же параметры шаблона, что и первая частичная специализация, и имеет только один параметр функции, тип которого — специализация шаблона класса со всеми аргументами шаблона первой частичной специализации
- второй шаблон функции имеет те же параметры шаблона, что и вторая частичная специализация, и имеет только один параметр функции, тип которого — специализация шаблона класса со всеми аргументами шаблона второй частичной специализации.
Затем шаблоны функций ранжируются так же, как и при перегрузке шаблонов функций.
template<int I, int J, class T> struct X {}; // primary template
template<int I, int J> struct X<I, J, int>
{
static const int s = 1;
}; // partial specialization #1
// fictitious function template for #1 is
// 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;
}; // partial specialization #2
// fictitious function template for #2 is
// template<int I> void f(X<I, I, int>); #B
int main()
{
X<2, 2, int> x; // both #1 and #2 match
// partial ordering for function templates:
// #A from #B: void(X<I, J, int>) from void(X<U1, U1, int>): deduction OK
// #B from #A: void(X<I, I, int>) from void(X<U1, U2, int>): deduction fails
// #B is more specialized
// #2 is the specialization that is instantiated
std::cout << x.s << '\n'; // prints 2
}Члены частичных специализаций
Список параметров шаблона и список аргументов шаблона члена частичной специализации должны совпадать со списком параметров и списком аргументов частичной специализации.
Так же, как и с членами основных шаблонов, они должны быть определены только при использовании в программе.
Члены частичных специализаций не связаны с членами основного шаблона.
Явная (полная) специализация члена частичной специализации объявляется так же, как и явная специализация основного шаблона.
template<class T, int I> // primary template
struct A
{
void f(); // member declaration
};
template<class T, int I>
void A<T, I>::f() {} // primary template member definition
// partial specialization
template<class T>
struct A<T, 2>
{
void f();
void g();
void h();
};
// member of partial specialization
template<class T>
void A<T, 2>::g() {}
// explicit (full) specialization
// of a member of partial specialization
template<>
void A<char, 2>::h() {}
int main()
{
A<char, 0> a0;
A<char, 2> a2;
a0.f(); // OK, uses primary template’s member definition
a2.g(); // OK, uses partial specialization's member definition
a2.h(); // OK, uses fully-specialized definition of
// the member of a partial specialization
a2.f(); // error: no definition of f() in the partial
// specialization A<T,2> (the primary template is not used)
}Если основной шаблон является членом другого шаблона класса, его частичные специализации являются членами окружающего шаблона класса. Если окружающий шаблон подлежит инстанцированию, объявление каждого члена частичной специализации также подлежит инстанцированию (так же, как объявления, но не определения, всех других членов шаблона подлежат инстанцированию).
Если основной шаблон-член явно (полностью) специализируется для заданной (явной) специализации окружающего шаблона класса, частичные специализации шаблона-члена игнорируются для этой специализации окружающего шаблона класса.
Если частичная специализация шаблона-члена явно специализируется для заданной (явной) специализации окружающего шаблона класса, основной шаблон-член и его другие частичные специализации по-прежнему рассматриваются для этой специализации окружающего шаблона класса.
template<class T> struct A // enclosing class template
{
template<class T2>
struct B {}; // primary member template
template<class T2>
struct B<T2*> {}; // partial specialization of member template
};
template<>
template<class T2>
struct A<short>::B {}; // full specialization of primary member template
// (will ignore the partial)
A<char>::B<int*> abcip; // uses partial specialization T2=int
A<short>::B<int*> absip; // uses full specialization of the primary (ignores partial)
A<char>::B<int> abci; // uses primaryОтчеты о дефектах
Следующие отчеты о дефектах, изменяющие поведение, были применены ретроактивно к ранее опубликованным стандартам 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 | недостающие ссылки на шаблоны переменных | добавить поддержку шаблонов переменных |
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/w/cpp/language/partial_specialization