Explicit (full) template specialization
Позволяет настраивать шаблонный код для заданного набора аргументов шаблона.
Содержание |
Синтаксис
template <>
объявление
|
|||||||||
Любая из следующих может быть полностью специализирована:
- шаблон функции
- шаблон класса
- шаблон переменной (начиная с C++14)
- функция-член шаблона класса
- статический член данных шаблона класса
- класс-член шаблона класса
- перечисление-член перечисления шаблона класса
- шаблон класса-члена класса или шаблона класса
- шаблон функции-члена класса или шаблона класса
- шаблон переменной-члена класса или шаблона класса (начиная с 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 .
|
Этот раздел не завершён
Причина: необходимо проверить требования к спецификациям исключений в разных версиях C++ |
Участники специализаций
При определении члена явно специализированного шаблона класса вне тела класса синтаксис 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>();
` и `` оставлен без изменений, как и требовалось в инструкциях. 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 |
было неясно, переносятся ли атрибуты основного
шаблона в его явные специализации |
не переносятся |