Spec-Zone.ru › C++

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

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

Синтаксис

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> // 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/w/cpp/language/template_specialization

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API