Spec-Zone.ru › C++

Частичная специализация шаблонов

Позволяет настраивать шаблоны классов и переменных(с 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, которая имеет частичную специализацию для типов массивов.

Список аргументов

Следующие ограничения применяются к списку аргументов частичной специализации шаблона:

1) Список аргументов не может быть идентичен списку аргументов неспециализированного шаблона (он должен специализировать что-то):
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)
2) Значения по умолчанию не могут появляться в списке аргументов.
3) Если какой-либо аргумент является расширением пакета, он должен быть последним аргументом в списке.
4) Выражения аргументов нетипового типа могут использовать параметры шаблона, если параметр появляется хотя бы один раз вне невыводимого контекста (обратите внимание, что в настоящее время эту функцию поддерживает только clang):
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
5) Аргументы шаблона нетипового типа не могут специализировать параметр шаблона, тип которого зависит от параметра специализации:
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 {}; // 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) подлежит инстанцированию, и доступны частичные специализации, компилятор должен определить, будет ли использоваться основной шаблон или одна из его частичных специализаций.

1) Если только одна специализация соответствует аргументам шаблона, используется эта специализация.
2) Если соответствует более одной специализации, используются правила частичного порядка, чтобы определить, какая специализация более специализированная. Используется самая специализированная специализация, если она единственная (если она не единственная, программа не может быть скомпилирована).
3) Если ни одна специализация не соответствует, используется основной шаблон.
// 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

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API