explicit template specialization

Allows customizing the template code for a given set of template arguments.

Syntax

template <> declaration

Any of the following can be fully specialized:

  1. function template
  2. class template
  3. (since C++14)variable template
  4. member function of a class template
  5. static data member of a class template
  6. member class of a class template
  7. member enumeration of a class template
  8. member class template of a class or class template
  9. member function template of a class or class template

For example,

#include <iostream>
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()
{
    // for any type T other than void, the 
    // class is derived from false_type
    std::cout << is_void<char>::value << '\n'; 
    // but when T is void, the class is derived
    // from true_type
    std::cout << is_void<void>::value << '\n';
}

In detail

Explicit specialization can only appear in the same namespace as the primary template, and has to appear after the non-specialized template declaration. It is always in the scope of that namespace:

namespace N {
    template<class T> class X { /*...*/ }; // primary template
    template<> class X<int> { /*...*/ }; // specialization in same namespace
 
    template<class T> class Y { /*...*/ }; // primary template
    template<> class Y<double>; // forward declare specialization for double
}
template<>
class N::Y<double> { /*...*/ }; // OK: specialization in same namespace

Specialization must be declared before the first use that would cause implicit instantiation, in every translation unit where such use occurs:

class String {};
template<class T> class Array { /*...*/ };
template<class T> void sort(Array<T>& v) { /*...*/ } // primary template
 
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

A template specialization that was declared but not defined can be used just like any other incomplete type (e.g. pointers and references to it may be used).

template<class T> class X; // primary template
template<> class X<int>; // specialization (declared, not defined)
X<int>* p; // OK: pointer to incomplete type
X<int> x; // error: object of incomplete type

Explicit specializations of function templates

When specializing a function template, its template arguments can be omitted if template argument deduction can provide them from the function arguments:

template<class T> class Array { /*...*/ };
template<class T> void sort(Array<T>& v); // primary template
template<> void sort(Array<int>&); // specialization for T = int
// no need to write
// template<> void sort<int>(Array<int>&);

A function with the same name and the same argument list as a specialization is not a specialization (see template overloading in function_template).

An explicit specialization of a function template is inline only if it is declared with the inline specifier (or defined as deleted), it doesn't matter if the primary template is inline.

Default function arguments cannot be specified in explicit specializations of function templates, member function templates, and member functions of class templates when the class is implicitly instantiated.

An explicit specialization cannot be a friend declaration.

Members of specializations

When defining a member of an explicitly specialized class template outside the body of the class, the syntax template <> is not used, except if it's a member of an explicitly specialized member class template, which is specialized as a class template, because otherwise, the syntax would require such definition to begin with template<parameters> required by the nested template.

template< typename T>
struct A {
    struct B {};  // member class 
    template<class U> struct C { }; // member class template
};
 
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() { /* ... */ }


An explicit specialization of a static data member of a template is a definition if the declaration includes an initializer; otherwise, it is a declaration. These definitions must use braces for default initialization:

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

A member or a member template of a class template may be explicitly specialized for a given implicit instantiation of the class template, even if the member or member template is defined in the class template definition.

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> void g1(T, X1); // member template
    template<class X2> void g2(T, X2); // member template
};
 
// 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);

Member or a member template may be nested within many enclosing class templates. In an explicit specialization for such a member, there's a template<> for every enclosing class template that is explicitly specialized.

template<class T1> class A {
    template<class T2> class B {
        void mf();
    };
};
template<> template<> class A<int>::B<double>;
template<> template<> void A<char>::B<char>::mf();

In such a nested declaration, some of the levels may remain unspecialized (except that it can't specialize a class member template if its enclosing class is unspecialized). For each of those levels, the declaration needs template<arguments>, because such specializations are themselves templates:

template <class T1> class A {
    template<class T2> class B {
        template<class T3> void mf1(T3); // member template
        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() { }

See also

doc_CPP
2016-10-11 09:58:37
Comments
Leave a Comment

Please login to continue.