16.6. Template Specializations

If we call this template definition on two const char* arguments, the function compares the pointer values. It will tell us the relative positions in memory of these two pointers but says nothing about the contents of the arrays to which the pointers point.To get be able to use compare with character strings, we would have to provide a specialized definition that knows how to compare C-style strings. The fact that these versions are specialized is transparent to users of these templates. Calls to a specialized function or use of a specialized class are indistinguishable from uses of a version instantiated from the general template.
template <typename T>
int compare(const T &v1, const T &v2)
{
if (v1 < v2) return -1;
if (v2 < v1) return 1;
return 0;
}
16.6.1. Specializing a Function Template
A template spacialization is a separate definition in which the actual type(s) or value(s) of one or more template parameter(s) is (are) specified. The form of a specialization is:The keyword template followed by an empty bracket pair (<>),followed by the template name and a bracket pair specifying the template parameters(s) that this specialization defines,the function parameter list,and the function body.
The following program defines a specialization of compare when the template parameter type is bound to const char*:
The declaration for the specialization must match that of the corresponding template. In this case, the template has one type parameter and two function parameters. The function parameters are const references to the type parameter. Here we are fixing the type parameter to const char*; our function parameters, therefore, are const references to a const char*.Now when we call compare, passing it two character pointers, the compiler will call our specialized version. It will call the generic version for any other argument types (including plain char*):
// special version of compare to handle C-style character strings
template <>
int compare<const char*>(const char* const &v1,
const char* const &v2)
{
return strcmp(v1, v2);
}
const char *cp1 = "world", *cp2 = "hi";
int i1, i2;
compare(cp1, cp2); // calls the specialization
compare(i1, i2); // calls the generic version instantiated with int
Declaring a Template Specialization
As with any function, we can declare a function template specialization without defining it. A template specialization declaration looks like the definition but omits the function body:
This declaration consists of an empty template parameter list (template<>) followed by the return type, the function name (optionally) followed by explicit template argument(s) specified inside a pair of angle brackets, and the function parameter list. A template specialization must always include the empty template parameter specifier, template<>, and it must include the function parameter list. If the template arguments can be inferred from the function parameter list, there is no need to explicitly specify the template arguments:
// declaration of function template explicit specialization
template<>
int compare<const char*>(const char* const&,
const char* const&);
// error: invalid specialization declarations
// missing template<>
int compare<const char*>(const char* const&,
const char* const&);
// error: function parameter list missing
template<> int compare<const char*>;
// ok: explicit template argument const char* deduced from parameter types
template<> int compare(const char* const&,
const char* const&);
Function Overloading versus Template Specializations
Omitting the empty template parameter list, template<>, on a specialization may have surprising effects. If the specialization syntax is missing, then the effect is to declare an overloaded nontemplate version of the function:
The definition of compare does not define a template specialization. Instead, it declares an ordinary function with a return type and a parameter list that could match those of a template instantiation.We'll look at the interaction of overloading and templates in more detail in the next section. For now, what's important to know is that when we define a nontemplate function, normal conversions are applied to the arguments. When we specialize a template, conversions are not applied to the argument types. In a call to a specialized version of a template, the argument type(s) in the call must match the specialized version function parameter type(s) exactly. If they don't, then the compiler will instantiate an instantiation for the argument(s) from the template definition.
// generic template definition
template <class T>
int compare(const T& t1, const T& t2) { /* ... */ }
// OK: ordinary function declaration
int compare(const char* const&, const char* const&);
Duplicate Definitions Cannot Always Be Detected
If a program consists of more than one file, the declaration for a template specialization must be visible in every file in which the specialization is used. A function template cannot be instantiated from the generic template definition in some files and be specialized for the same set of template arguments in other files.

Ordinary Scope Rules Apply to Specializations
Before we can declare or define a specialization, a declaration for the template that it specializes must be in scope. Similarly, a declaration for the specialization must be in scope before that version of the template is called:
This program is in error because a call that would match the specialization is made before the specialization is declared. When the compiler sees a call, it must know to expect a specialization for this version. Otherwise, the compiler is allowed to instantiate the function from the template definition.
// define the general compare template
template <class T>
int compare(const T& t1, const T& t2) { /* ... */ }
int main() {
// uses the generic template definition
int i = compare("hello", "world");
// ...
}
// invalid program: explicit specialization after call
template<>
int compare<const char*>(const char* const& s1,
const char* const& s2)
{ /* ... */ }

Exercises Section 16.6.1
Exercise 16.52:Define a function template count to count the number of occurrences of some value in a vector.Exercise 16.53:Write a program to call the count function defined in the previous exercise passing it first a vector of doubles, then a vector of ints, and finally a vector of chars.Exercise 16.54:Introduce a specialized template instance of the count function to handle strings. Rerun the program you wrote to call the function template instantiations.
16.6.2. Specializing a Class Template
Our Queue class has a problem similar to the one in compare when used with C-style strings. In this case, the problem is in the push function. That function copies the value it's given to create a new element in the Queue. By default, copying a C-style character string copies only the pointer, not the characters. Copying a pointer in this case has all the problems that shared pointers have in other contexts. The most serious is that if the pointer points to dynamic memory, it's possible for the user to delete the array to which the pointer points.
Defining a Class Specialization
One way to provide the right behavior for Queue's of C-style strings is to define a specialized version of the entire class for const char*:
This implementation gives Queue a single data element: a Queue of strings. The various members delegate their work to this memberfor example, pop is implemented by calling pop on real_queue.This version of the class does not define the copy-control members. Its only data element has a class type that does the right thing when copied, assigned, or destroyed; we can use the synthesized copy-control members.Our Queue class implements mostly, but not entirely, the same interface as the template version of Queue. The difference is that we return a string rather than a char* from the front members. We do so to avoid having to manage the character array that would be required if we wanted to return a pointer.It is worth noting that a specialization may define completely different members than the template itself. If a specialization fails to define a member from the template, that member may not be used on objects of the specilization type. The member definitions of the class template are not used to create the definitions for the members of an explicit specialization.
/* definition of specialization for const char*
* this class forwards its work to Queue<string>;
* the push function translates the const char* parameter to a string
* the front functions return a string rather than a const char*
*/
template<> class Queue<const char*> {
public:
// no copy control: Synthesized versions work for this class
// similarly, no need for explicit default constructor either
void push(const char*);
void pop() {real_queue.pop();}
bool empty() const {return real_queue.empty();}
// Note: return type does not match template parameter type
std::string front() {return real_queue.front();}
const std::string &front() const
{return real_queue.front();}
private:
Queue<std::string> real_queue; // forward calls to real_queue
};

Class Specialization Definition

Although it does little obvious work, this function implicitly copies the character array to which val points. The copy is made in the call to real_queue.push, which creates a new string from the const char* argument. That argument uses the string constructor that takes a const char*. The string constructor copies the characters from the array pointed to by val into an unnamed string that will be stored in the element we push onto real_queue.
void Queue<const char*>::push(const char* val)
{
return real_queue.push(val);
}
Exercises Section 16.6.2
Exercise 16.55:The comments on the specialized version of Queue for const char* note that there is no need to define the default constructor or copy-control members. Explain why the synthesized members suffice for this version of Queue.Exercise 16.56:We explained the generic behavior of Queue if it is not specialized for const char*. Using the generic Queue template, explain what happens in the following code:
In particular, say what the values of q1 and q2 are after the initialization of q2 and after the assignment to q3.Exercise 16.57:Our specialized Queue returns strings from the front function rather than const char*. Why do you suppose we did so? How might you implement the Queue to return a const char*? Discuss the pros and cons of each approach.
Queue<const char*> q1;
q1.push("hi"); q1.push("bye"); q1.push("world");
Queue<const char*> q2(q1); // q2 is a copy of q1
Queue<const char*> q3; // empty Queue
q1 = q3;
16.6.3. Specializing Members but Not the Class
If we look a bit more deeply at our class, we can see that we can simplify our code: Rather than specializing the whole template, we can specialize just the push and pop members. We'll specialize push to copy the character array and pop to free the memory we used for that copy:
Now, the class type Queue<const char*> will be instantiated from the generic class template definition, with the exception of the push and pop functions. When we call push or pop on a Queue<const char*>, then the specialized version will be called. When we use any other member, the generic one will be instantiated for const char* from the class template.
template <>
void Queue<const char*>::push(const char *const &val)
{
// allocate a new character array and copy characters from val
char* new_item = new char[strlen(val) + 1];
strncpy(new_item, val, strlen(val) + 1);
// store pointer to newly allocated and initialized element
QueueItem<const char*> *pt =
new QueueItem<const char*>(new_item);
// put item onto existing queue
if (empty())
head = tail = pt; // queue has only one element
else {
tail->next = pt; // add new element to end of queue
tail = pt;
}
}
template <>
void Queue<const char*>::pop()
{
// remember head so we can delete it
QueueItem<const char*> *p = head;
delete head->item; // delete the array allocated in push
head = head->next; // head now points to next element
delete p; // delete old head element
}
Specialization Declarations
Member specializations are declared just as any other function template specialization. They must start with an empty template parameter list:
These declarations should be placed in the Queue header file.
// push and pop specialized for const char*
template <>
void Queue<const char*>::push(const char* const &);
template <> void Queue<const char*>::pop();
Exercises Section 16.6.3
Exercise 16.58:The specialization of Queue presented in the previous subsection and the specialization in this subsection of push and pop apply only to Queues of const char*. Implement these two different ways of specializing Queue that could be used with plain char*.Exercise 16.59:If we go the route of specializing only the push function, what value is returned by front for a Queue of C-style character strings?Exercise 16.60:Discuss the pros and cons of the two designs: defining a specialized version of the class for const char* versus specializing only the push and pop functions. In particular, compare and contrast the behavior of front and the possibility of errors in user code corrupting the elements in the Queue.
16.6.4. Class-Template Partial Specializations
If a class template has more than one template parameter, we might want to specialize some but not all of the template parameters. We can do so using a class template partial specialization:
A class template partial specialization is itself a template. The definition of a partial specialization looks like a template definition. Such a definition begins with the keyword template followed by a template parameter list enclosed by angle brackets (<>). The parameter list of a partial specialization is a subset of the parameter list of the corresponding class template definition. The partial specialization for some_template has only one template type parameter named T1. The second template argument for T2 is known to be int. The template parameter list for the partial specialization only lists the parameters for which the template arguments are still unknown.
template <class T1, class T2>
class some_template {
// ...
};
// partial specialization: fixes T2 as int and allows T1 to vary
template <class T1>
class some_template<T1, int> {
// ...
};
Using a Class-Template Partial Specialization
The partial specialization has the same name as the class template to which it correspondsnamely, some_template. The name of the class template must be followed by a template argument list. In the previous example, the template argument list is <T1,int>. Because the argument value for the first template parameter is unknown, the argument list uses the name of the template parameter T1 as a placeholder. The other argument is the type int, for which the template is partially specialized.As with any other class template, a partial specialization is instantiated implicitly when used in a program:
Notice that the type of the second variable, some_template parameterized by string and int, could be instantiated from the generic class template definition as well as from the partial specialization. Why is it that the partial specialization is chosen to instantiate the template? When a parital specialization is declared, the compiler chooses the template definition that is the most specialized for the instantiation. When no partial specialization can be used, the generic template definition is used. The instantiated type of foo does not match the partial specialization provided. Thus, the type of foo must be instantiated from the general class template, binding int to T1 and string to T2. The partial specialization is only used to instantiate some_template types with a second type of int.The definition of a partial specialization is completely disjointed from the definition of the generic template. The partial specialization may have a completely different set of members from the generic class template. The generic definitions for the members of a class template are never used to instantiate the members of the class template partial specialization.
some_template<int, string> foo; // uses template
some_template<string, int> bar; // uses partial specialization