Template Specializations

Class templates are like function templates in that you can have implicit instantiations, explicit instantiations, and explicit specializations, collectively known as specializations. That is, a template describes a class in terms of a general type, whereas a specialization is a class declaration generated by using a specific type.

Implicit Instantiations

The template examples you have seen so far in this chapter use implicit instantiations. That is, they declare one or more objects indicating the desired type, and the compiler generates a specialized class definition, using the recipe provided by the general template:

ArrayTP<int, 100> stuff; // implicit instantiation

The compiler doesn’t generate an implicit instantiation of the class until it needs an object:

ArrayTP<double, 30> * pt;     // a pointer, no object needed yet
pt = new ArrayTP<double, 30>; // now an object is needed

The second statement causes the compiler to generate a class definition and also an object that is created according to that definition.

Explicit Instantiations

The compiler generates an explicit instantiation of a class declaration when you declare a class by using the keyword template and indicating the desired type or types. The declaration should be in the same namespace as the template definition. For example, the following declaration declares ArrayTP<string, 100> to be a class:

template class ArrayTP<string, 100>; // generate ArrayTP<string, 100> class

In this case, the compiler generates the class definition, including method definitions, even though no object of the class has yet been created or mentioned. Just as with the implicit instantiation, the general template is used as a guide to generate the specialization.

Explicit Specializations

An explicit specialization is a definition for a particular type or types that is to be used instead of the general template. Sometimes you might need or want to modify a template to behave differently when instantiated for a particular type; in that case, you can create an explicit specialization. Suppose, for example, that you’ve defined a template for a class that represents a sorted array for which items are sorted as they are added to the array:

template <typename T>
class SortedArray
{
     ...// details omitted
};

Also suppose the template uses the > operator to compare values. This works well for numbers. It will work if T represents a class type, too, provided that you’ve defined a T::operator>() method. But it won’t work if T is a string represented by type const char *. Actually, the template will work, but the strings will wind up sorted by address rather than alphabetically. What is needed is a class definition that uses strcmp() instead of >. In such a case, you can provide an explicit template specialization. This takes the form of a template defined for one specific type instead of for a general type. When faced with the choice of a specialized template and a general template that both match an instantiation request, the compiler uses the specialized version.

A specialized class template definition has the following form:

template <> class Classname<specialized-type-name> { ... };

Older compilers may only recognize the older form, which dispenses with the template <> prefix:

class Classname<specialized-type-name> { ... };

To provide a SortedArray template specialized for the const char * type, using the current notation, you would use code like the following:

template <> class SortedArray<const char char *>
{
     ...// details omitted
};

Here the implementation code would use strcmp() instead of > to compare array values. Now, requests for a SortedArray template of const char * will use this specialized definition instead of the more general template definition:

SortedArray<int> scores;          // use general definition
SortedArray<const char *> dates;  // use specialized definition

Partial Specializations

C++ allows for partial specializations, which partially restrict the generality of a template. For example, a partial specialization can provide a specific type for one of the type parameters:

// general template
    template <class T1, class T2> class Pair {...};
// specialization with T2 set to int
    template <class T1> class Pair<T1, int> {...};

The <> following the keyword template declares the type parameters that are still unspecialized. So the second declaration specializes T2 to int but leaves T1 open. Note that specifying all the types leads to an empty bracket pair and a complete explicit specialization:

// specialization with T1 and T2 set to int
    template <> class Pair<int, int> {...};

The compiler uses the most specialized template if there is a choice. Here’s what would happen given the preceding three templates:

Pair<double, double> p1; // use general Pair template
Pair<double, int> p2;    // use Pair<T1, int> partial specialization
Pair<int, int> p3;       // use Pair<int, int> explicit specialization

Or you can partially specialize an existing template by providing a special version for pointers:

template<class T>        // general version
class Feeb { ... };
template<class T*>       // pointer partial specialization
class Feeb { ... };      // modified code

If you provide a non-pointer type, the compiler uses the general version; if you provide a pointer, the compiler uses the pointer specialization:

Feeb<char> fb1;          // use general Feeb template, T is char
Feeb<char *> fb2;        // use Feeb T* specialization, T is char

Without the partial specialization, the second declaration would use the general template, interpreting T as type char *. With the partial specialization, it uses the specialized template, interpreting T as char.

The partial specialization feature allows for making a variety of restrictions. For example, you can use the following:

// general template
    template <class T1, class T2, class T3> class Trio{...};
// specialization with T3 set to T2
    template <class T1, class T2> class Trio<T1, T2, T2> {...};
// specialization with T3 and T2 set to T1*
    template <class T1> class Trio<T1, T1*, T1*> {...};

Given these declarations, the compiler would make the following choices:

Trio<int, short, char *> t1; // use general template
Trio<int, short> t2; // use Trio<T1, T2, T2>
Trio<char, char *, char *> t3; use Trio<T1, T1*, T1*>

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.137.188.201