Явная (полная) специализация шаблона
Позволяет настраивать код шаблона для заданного набора аргументов шаблона.
Синтаксис
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> // primary template
class X { /*...*/ };
template<> // specialization in same namespace
class X<int> { /*...*/ };
template<class T> // primary template
class Y { /*...*/ };
template<> // forward declare specialization for double
class Y<double>;
}
template<> // OK: specialization in same namespace
class N::Y<double> { /*...*/ };
Специализация должна быть объявлена до первого использования, которое вызвало бы неявное создание экземпляра, в каждой единице трансляции, где такое использование происходит:
class String {};
template<class T>
class Array { /*...*/ };
template<class T> // primary template
void sort(Array<T>& v) { /*...*/ }
void f(Array<String>& v)
{
sort(v); // implicitly instantiates sort(Array<String>&),
} // using the primary template for sort()
template<> // ERROR: explicit specialization of sort(Array<String>)
void sort<String>(Array<String>& v); // after implicit instantiation
Объявленную, но не определённую специализацию шаблона можно использовать так же, как любой другой неполный тип (например, можно использовать указатели и ссылки на неё):
template<class T> // primary template class X; template<> // specialization (declared, not defined) class X<int>; X<int>* p; // OK: pointer to incomplete type X<int> x; // error: object of incomplete type
Является ли явная специализация шаблона функции или переменной(начиная с C++14) inline/constexpr(начиная с C++11)/constinit/consteval(начиная с C++20), определяется самой явной специализацией, независимо от того, объявлен ли первичный шаблон с таким спецификатором. Аналогично, атрибуты, присутствующие в объявлении шаблона, не влияют на явную специализацию этого шаблона:(начиная с 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, not inline
template<typename>
[[noreturn]] void h([[maybe_unused]] int i);
template<> void h<int>(int i)
{
// [[noreturn]] has no effect, but [[maybe_unused]] has
}
Явные специализации шаблонов функций
При специализации шаблона функции аргументы шаблона можно опустить, если выведение аргументов шаблона позволяет получить их из аргументов функции:
template<class T>
class Array { /*...*/ };
template<class T> // primary template
void sort(Array<T>& v);
template<> // specialization for T = int
void sort(Array<int>&);
// no need to write
// template<> void sort<int>(Array<int>&);
Функция с тем же именем и тем же списком аргументов, что и специализация, не является специализацией (см. перегрузку шаблонов в разделе «Шаблон функции»).
Аргументы функции по умолчанию нельзя указывать в явных специализациях шаблонов функций, шаблонов функций-членов и функций-членов шаблонов классов, если класс создаётся неявно.
Явная специализация не может быть объявлением-другом.
Члены специализаций
При определении члена явно специализированного шаблона класса вне тела класса синтаксис template<> не используется, кроме случая, когда это член явно специализированного шаблона класса-члена, который специализируется как шаблон класса, поскольку в противном случае синтаксис требовал бы, чтобы такое определение начиналось с template<parameters>, необходимого для вложенного шаблона
template<typename T>
struct A
{
struct B {}; // member class
template<class U> // member class template
struct C {};
};
template<> // specialization
struct A<int>
{
void f(int); // member function of a specialization
};
// template<> not used for a member of a specialization
void A<int>::f(int) { /* ... */ }
template<> // specialization of a member class
struct A<char>::B
{
void f();
};
// template<> not used for a member of a specialized member class either
void A<char>::B::f() { /* ... */ }
template<> // specialization of a member class template
template<class U>
struct A<char>::C
{
void f();
};
// template<> is used when defining a member of an explicitly
// specialized member class template specialized as a class template
template<>
template<class U>
void A<char>::C<U>::f() { /* ... */ }
Явная специализация статического члена-данных шаблона является определением, если объявление содержит инициализатор; в противном случае это объявление. В этих определениях для инициализации по умолчанию необходимо использовать фигурные скобки:
template<>
X Q<int>::x; // declaration of a static member
template<>
X Q<int>::x (); // error: function declaration
template<>
X Q<int>::x {}; // definition of a default-initialized static member
Член или шаблон-член шаблона класса может быть явно специализирован для заданного неявного создания экземпляра шаблона класса, даже если член или шаблон-член определён в определении шаблона класса.
template<typename T>
struct A
{
void f(T); // member, declared in the primary template
void h(T) {} // member, defined in the primary template
template<class X1> // member template
void g1(T, X1);
template<class X2> // member template
void g2(T, X2);
};
// specialization of a member
template<>
void A<int>::f(int);
// member specialization OK even if defined in-class
template<>
void A<int>::h(int) {}
// out of class member template definition
template<class T>
template<class X1>
void A<T>::g1(T, X1) {}
// member template specialization
template<>
template<class X1>
void A<int>::g1(int, X1);
// member template specialization
template<>
template<>
void A<int>::g2<char>(int, char); // for X2 = char
// same, using template argument deduction (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<arguments>, поскольку такие специализации сами являются шаблонами:
template<class T1>
class A
{
template<class T2>
class B
{
template<class T3> // member template
void mf1(T3);
void mf2(); // non-template member
};
};
// specialization
template<> // for the specialized A
template<class X> // for the unspecialized B
class A<int>::B
{
template<class T>
void mf1(T);
};
// specialization
template<> // for the specialized A
template<> // for the specialized B
template<class T> // for the unspecialized mf1
void A<int>::B<double>::mf1(T t) {}
// ERROR: B<double> is specialized and is a member template, so its enclosing A
// must be specialized also
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 | не было ясно, наследуются ли атрибуты первичного шаблона его явными специализациями | не наследуются |
См. также
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/cpp/language/template_specialization