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

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

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

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

Содержание

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

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

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

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

Например,

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

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

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

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

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

template<class T>void f(T){/* ... */}template<>inlinevoid 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<>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<>// для специализированного Atemplate<class X>// для неспециализированного Bclass A<int>::B{template<class T>void mf1(T);};   // специализацияtemplate<>// для специализированного Atemplate<>// для специализированного Btemplate<class T>// для неспециализированной mf1void 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 2478 C++20 было неясно, переносятся ли constinit и consteval основного шаблона в его явные специализации не переносятся
CWG 2604 C++11 было неясно, переносятся ли атрибуты основного шаблона в его
явные специализации
не переносятся

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

close