Spec-Zone.ru › C++

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

Позволяет настраивать шаблоны классов и переменных(начиная с C++14) для заданной категории аргументов шаблона.

Синтаксис

template < parameter-list > class-key class-head-name < argument-list > declaration (1)
template < parameter-list > decl-specifier-seq declarator < argument-list > initializer (необязательно) (2) (начиная с C++14)

где class-head-name задаёт имя ранее объявленного шаблона класса и declarator задаёт имя ранее объявленного шаблона переменной(начиная с 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, для которого предусмотрена частичная специализация для типов массивов.

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

К argument-list частичной специализации шаблона применяются следующие ограничения:

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 и gcc 12):
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/cpp/language/partial_specialization

Spec-Zone.ru

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