Namespaces
Variants

Partial template specialization

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

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

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) Если любой аргумент является pack expansion, он должен быть последним аргументом в списке
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 {}; // основной шаблон
}
using N::Z; // ссылается на основной шаблон
namespace N
{
    template<class T> class Z<T, T*> {};     // частичная специализация
}
Z<int, int*> z; // поиск имени находит N::Z (основной шаблон), затем
                // используется частичная специализация с T = int

Частичное упорядочивание

Когда шаблон класса или переменной (since 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».

Формально, для установления отношения «более специализированная, чем» между частичными специализациями каждая из них сначала преобразуется в фиктивный шаблон функции следующим образом:

  • первая шаблонная функция имеет те же параметры шаблона, что и первая частичная специализация, и имеет только один параметр функции, тип которого представляет собой специализацию шаблона класса со всеми аргументами шаблона из первой частичной специализации
  • вторая шаблонная функция имеет те же параметры шаблона, что и вторая частичная специализация, и имеет только один параметр функции, тип которого представляет собой специализацию шаблона класса со всеми аргументами шаблона из второй частичной специализации

Шаблоны функций затем ранжируются как при 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 отсутствовали ссылки на шаблоны переменных добавлена поддержка шаблонов переменных

Смотрите также