Пространства имён
Варианты
Действия

Явная (полная) специализация шаблона

Материал из cppreference.com
< cpp‎ | language
 
 
Язык С++
Общие темы
Управление потоком
Операторы условного выполнения
if
Операторы итераций (циклы)
Операторы переходов
Функции
Объявление функции
Выражение лямбда-функции
Спецификатор inline
Спецификации динамических исключений (до C++20)
Спецификатор noexcept (C++11)
Исключения
Пространства имён
Типы
Спецификаторы
decltype (C++11)
auto (C++11)
alignas (C++11)
Спецификаторы длительности хранения
Инициализация
Выражения
Альтернативные представления
Литералы
Логические - Целочисленные - С плавающей запятой
Символьные - Строковые - nullptr (C++11)
Определённые пользователем (C++11)
Утилиты
Атрибуты (C++11)
Типы
Объявление typedef
Объявление псевдонима типа (C++11)
Приведения
Неявные преобразования - Явные преобразования
static_cast - dynamic_cast
const_cast - reinterpret_cast
Выделение памяти
Классы
Свойства функции, зависящие от класса
explicit (C++11)
static
Специальные функции-элементы
Шаблоны
Специализация шаблона
Пакеты параметров (C++11)
Разное
 
 

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

Содержание

[править] Синтаксис

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

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

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

Например,

#include <iostream>
template<typename T>   // первичный шаблон
struct is_void : std::false_type
{
};
template<>  // явная специализация для T = void
struct is_void<void> : std::true_type
{
};
int main()
{
    // для любого типа T, кроме void, 
    // класс является производным от false_type
    std::cout << is_void<char>::value << '\n'; 
    // но когда T имеет тип void, класс является
    // производным от true_type
    std::cout << is_void<void>::value << '\n';
}


[править] В деталях

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

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

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

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; // ошибка: объект неполного типа

[править] Явные специализации шаблонов функций

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

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

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

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

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

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

[править] Элементы специализаций

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

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>();

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

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++:

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

[править] Смотрите также