Class Template Member Specialization





Item 48. Class Template Member Specialization

A common misconception about class template explicit specialization and partial specialization is that a specialization somehow "inherits" something from the primary template. This is not the case. A complete or partial specialization of a class template is a totally separate entity from the primary template and does not "inherit" either interface or implementation from the primary template. However, in a nontechnical sense, specializations do inherit a set of expectations about their interfaces and behaviors, in that users who write generic code to the interface of a primary template generally expect that code to work with specializations as well.

This implies that a complete or partial specialization must generally reimplement all the capabilities of the primary template, even if only a portion of the implementation requires customization. An alternative is often to specialize only a subset of the primary template's member functions. For example, consider the primary Heap template (see Class Template Explicit Specialization [46, 155]):

template <typename T>
class Heap {
  public:
    void push( const T &val );
    T pop();
    bool empty() const { return h_.empty(); }
  private:
    std::vector<T> h_;
};

Our complete specialization of Heap for const char * replaced the entire implementation of the primary, even though its private implementation and empty member function were perfectly adequate for a heap of character pointers. All we really had to do was specialize the push and pop member functions:

template <>
void Heap<const char *>::push( const char *const &pval ) {
    h_.push_back(pval);
    std::push_heap( h_.begin(), h_.end(), strLess );
}

template<>
const char *Heap<const char *>::pop() {
    std::pop_heap( h_.begin(), h_.end(), strLess );
    const char *tmp = h_.back(); h_.pop_back();
    return tmp;
}

These functions are explicit specializations of the corresponding members of the primary Heap template and will be used in place of the implicitly instantiated versions for Heap<const char *>.

Note that the interface of each of these functions must match exactly the corresponding interface in the template whose members they're specializing. For example, the primary template declared push to take an argument of type const T &, so the explicit specialization of push for const char * must have an argument type of const char * const &. (That's a reference to a const pointer to a const char.) Note that we didn't have this restriction when providing a complete specialization of the Heap template as a whole, where the argument to push was declared to be simply const char *.

To increase the level of complexity (a common occurrence when programming with templates), let's consider what would happen if we had available our partial specialization of Heap for pointers in general (see Template Partial Specialization [47, 161]):

template <typename T>
class Heap<T *> {
    //...
    void push( T *pval );
    //...
};

If this partial specialization of Heap is present, our explicit specialization of push now must conform to the interface of the push member of the partial specialization, since that's the function that would otherwise be instantiated for Heap<const char *>. The explicit specialization must now be declared as follows:

template <>
void Heap<const char *>::push( const char *pval ) {
    h_.push_back(pval);
    std::push_heap( h_.begin(), h_.end(), strLess );
}

Two final notes: First, other members of class templates may be explicitly specialized in addition to member functions, including static members and member templates.

Second, there is often confusion about the difference between explicit specialization and explicit instantiation. As we've seen in this item, explicit specialization is a means of providing a custom version of a template or template member that differs from what one would have gotten from an implicit instantiation. Explicit instantiation simply tells the compiler, explicitly, to instantiate a member that is identical to what one would have gotten with an implicit instantiation.

template void Heap<double>::push( const double & );

See also You Instantiate What You Use [61, 225].


     Python   SQL   Java   php   Perl 
     game development   web development   internet   *nix   graphics   hardware 
     telecommunications   C++ 
     Flash   Active Directory   Windows