Namespaces
Variants

Explicit (full) 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
Template specialization
Parameter packs (C++11)
Miscellaneous

Позволяет настраивать шаблонный код для заданного набора аргументов шаблона.

Содержание

Синтаксис

template <> объявление

Любая из следующих может быть полностью специализирована:

  1. шаблон функции
  2. шаблон класса
  3. шаблон переменной (начиная с C++14)
  4. функция-член шаблона класса
  5. статический член данных шаблона класса
  6. класс-член шаблона класса
  7. перечисление-член перечисления шаблона класса
  8. шаблон класса-члена класса или шаблона класса
  9. шаблон функции-члена класса или шаблона класса
  10. шаблон переменной-члена класса или шаблона класса (начиная с C++14)

Например,

#include <type_traits>
template<typename T> // primary template
struct is_void : std::false_type {};
template<>           // explicit specialization for T = void
struct is_void<void> : std::true_type {};
int main()
{
    static_assert(is_void<char>::value == false,
        "for any type T other than void, the class is derived from false_type");
    static_assert(is_void<void>::value == true,
        "but when T is void, the class is derived from true_type");
}

Подробно

Явная специализация может быть объявлена в любой области видимости, где может быть определена её основная шаблонная структура (что может отличаться от области видимости, где определена основная шаблонная структура; например, при внеклассовой специализации шаблона-члена ). Явная специализация должна появляться после объявления неспециализированного шаблона.

namespace N
{
    template<class T> // основной шаблон
    class X { /*...*/ };
    template<>        // специализация в том же пространстве имен
    class X<int> { /*...*/ };
    template<class T> // основной шаблон
    class Y { /*...*/ };
    template<>        // предварительное объявление специализации для double
    class Y<double>;
}
template<> // OK: специализация в том же пространстве имен
class N::Y<double> { /*...*/ };

Специализация должна быть объявлена до первого использования, которое вызвало бы неявную инстанциацию, в каждой единице трансляции, где такое использование происходит:

class String {};
template<class T>
class Array { /*...*/ };
template<class T> // основная шаблонная функция
void sort(Array<T>& v) { /*...*/ }
void f(Array<String>& v)
{
    sort(v); // неявно инстанцирует sort(Array<String>&), 
}            // используя основную шаблонную функцию для sort()
template<> // ОШИБКА: явная специализация sort(Array<String>)
void sort<String>(Array<String>& v); // после неявной инстанциации

Шаблонная специализация, которая была объявлена, но не определена, может использоваться так же, как и любой другой неполный тип (например, могут использоваться указатели и ссылки на неё):

template<class T> // основной шаблон
class X;
template<>        // специализация (объявлена, но не определена)
class X<int>;
X<int>* p; // OK: указатель на неполный тип
X<int> x;  // ошибка: объект неполного типа

Является ли явная специализация шаблона функции или переменной (since C++14) inline / constexpr (since C++11) / constinit / consteval (since C++20) определяется самой явной специализацией, независимо от того, объявлен ли основной шаблон с этим спецификатором. Аналогично, атрибуты присутствующие в объявлении шаблона не влияют на явную специализацию этого шаблона: (since C++11)

template<class T>
void f(T) { /* ... */ }
template<>
inline void f<>(int) { /* ... */ } // OK, inline
template<class T>
inline T g(T) { /* ... */ }
template<>
int g<>(int) { /* ... */ }         // OK, не inline
template<typename>
[[noreturn]] void h([[maybe_unused]] int i);
template<> void h<int>(int i)
{
    // [[noreturn]] не имеет эффекта, но [[maybe_unused]] имеет
}

Явные специализации шаблонов функций

При специализации шаблона функции аргументы шаблона могут быть опущены, если вывод аргументов шаблона может определить их из аргументов функции:

template<class T>
class Array { /*...*/ };
template<class T> // основная шаблонная функция
void sort(Array<T>& v);
template<>        // специализация для T = int
void sort(Array<int>&);
// нет необходимости писать
// template<> void sort<int>(Array<int>&);

Функция с тем же именем и тем же списком аргументов, что и специализация, не является специализацией (см. перегрузку шаблонов в function template ).

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

Явная специализация не может быть friend declaration .

Участники специализаций

При определении члена явно специализированного шаблона класса вне тела класса синтаксис template <> не используется, за исключением случая, когда это член явно специализированного шаблона класса-члена, который специализируется как шаблон класса, поскольку в противном случае синтаксис потребовал бы, чтобы такое определение начиналось с template < parameters > требуемого вложенным шаблоном

template<typename T>
struct A
{
    struct B {};      // член-класс
    template<class U> // шаблон член-класса
    struct C {};
};
template<> // специализация
struct A<int> 
{
    void f(int); // функция-член специализации
};
// template<> не используется для члена специализации
void A<int>::f(int) { /* ... */ }
template<> // специализация член-класса
struct A<char>::B
{
    void f();
};
// template<> также не используется для члена специализированного член-класса
void A<char>::B::f() { /* ... */ }
template<> // специализация шаблона член-класса
template<class U>
struct A<char>::C
{
    void f();
};
// template<> используется при определении члена явно
// специализированного шаблона член-класса, специализированного как шаблон класса
template<>
template<class U>
void A<char>::C<U>::f() { /* ... */ }


Явная специализация статического члена данных шаблона является определением, если объявление включает инициализатор; в противном случае это объявление. Эти определения должны использовать фигурные скобки для инициализации по умолчанию:

template<>
X Q<int>::x;    // объявление статического члена
template<>
X Q<int>::x (); // ошибка: объявление функции
template<>
X Q<int>::x {}; // определение статического члена с инициализацией по умолчанию

Член или шаблон-член шаблона класса может быть явно специализирован для заданной неявной инстанциации шаблона класса, даже если член или шаблон-член определен в определении шаблона класса.

template<typename T>
struct A
{
    void f(T);         // член, объявленный в первичном шаблоне
    void h(T) {}       // член, определенный в первичном шаблоне
    template<class X1> // шаблон-член
    void g1(T, X1);
    template<class X2> // шаблон-член
    void g2(T, X2);
};
// специализация члена
template<>
void A<int>::f(int);
// специализация члена допустима даже если определена внутри класса
template<>
void A<int>::h(int) {}
// внешнее определение шаблона-члена
template<class T>
template<class X1>
void A<T>::g1(T, X1) {}
// специализация шаблона-члена
template<>
template<class X1>
void A<int>::g1(int, X1);
// специализация шаблона-члена
template<>
template<>
void A<int>::g2<char>(int, char); // для X2 = char
// то же, с использованием вывода аргументов шаблона (X1 = char)
template<> 
template<>
void A<int>::g1(int, char);

Член или шаблон члена может быть вложен в несколько охватывающих шаблонов классов. В явной специализации для такого члена присутствует template <> для каждого охватывающего шаблона класса, который явно специализирован.

template<class T1>
struct A
{
    template<class T2>
    struct B
    {
        template<class T3>
        void mf();
    };
};
template<>
struct A<int>;
template<>
template<>
struct A<char>::B<double>;
template<>
template<>
template<>
void A<char>::B<char>::mf<double>();
**Примечание:** Весь код C++ внутри тегов `
` и `` оставлен без изменений, как и требовалось в инструкциях. HTML-разметка и атрибуты также сохранены в оригинальном виде.

В такой вложенной декларации некоторые уровни могут оставаться неспециализированными (за исключением того, что нельзя специализировать шаблон-член класса в области видимости пространства имен, если его объемлющий класс не специализирован). Для каждого из этих уровней в декларации требуется template < arguments > , поскольку такие специализации сами являются шаблонами:

template<class T1>
class A
{
    template<class T2>
    class B
    {
        template<class T3> // шаблонный член
        void mf1(T3);
        void mf2();        // нешаблонный член
    };
};
// специализация
template<>        // для специализированного A
template<class X> // для неспециализированного B
class A<int>::B
{
    template<class T>
    void mf1(T);
};
// специализация
template<>        // для специализированного A
template<>        // для специализированного B
template<class T> // для неспециализированного mf1
void A<int>::B<double>::mf1(T t) {}
// ОШИБКА: B<double> специализирован и является шаблонным членом, поэтому его включающий A
// также должен быть специализирован
template<class Y>
template<>
void A<Y>::B<double>::mf2() {}

Отчеты о дефектах

Следующие отчеты об изменениях поведения, влияющие на дефекты, были применены задним числом к ранее опубликованным стандартам C++.

DR Применяется к Поведение в опубликованной версии Корректное поведение
CWG 531 C++98 синтаксис определения членов явных
специализаций в области видимости пространства имён не был определён
определён
CWG 727 C++98 частичные и полные специализации не разрешались в
области видимости класса
разрешены в любой области видимости
CWG 730 C++98 шаблоны-члены нешаблонных
классов не могли быть полностью специализированы
разрешено
CWG 2478 C++20 было неясно, переносятся ли constinit и consteval из
основного шаблона в его явные специализации
не переносятся
CWG 2604 C++11 было неясно, переносятся ли атрибуты основного
шаблона в его явные специализации
не переносятся

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