C++ Template Specialization
C++ requires us to use specific types to declare variables, functions, and other entities. However, a lot of code looks the same for different types. Especially if we implement algorithms, such as quicksort, or the behaviour of data structures, such as a linked list or a binary tree for different types, the code looks the same despite the type used. Suppose our programming language doesn't support a special language feature for this. In that case, we only have bad alternatives:
If we come from C, Java, or similar languages, we have done some or all of this before. However, each of these approaches has its drawbacks:
Templates are a solution to this problem without these drawbacks. They are functions or classes written for one or more types yet to be specified. When we use a template, we pass the types as arguments, explicitly or implicitly. Because templates are language features, we have full type-checking and scope support. In today's programs, templates are used a lot. For example, inside the C++ standard library, almost all code is template code. The library provides sort algorithms to sort objects and values of a specified type, data structures (so-called container classes) to manage elements of a selected type, strings for which the style of a character is parameterized, and so on. However, this is only the beginning. Templates also allow us to parameterize behaviour, optimize code, and parameterize information. It is covered in later chapters. Let's first start with some simple templates.
Specializations of Class Templates
We can specialize in a class template for certain template arguments. Like the overloading of function templates, specializing in class templates allows we to optimize implementations for certain types or fix the mis-behavior of certain types to instantiate the class template. However, if we specialize in a class template, we must also specialize in all member functions. Although it is possible to specialize in a single member function, once we have done so, we can no longer specialize the whole class.
To specialize a class template, we must declare the class with a leading template<> and a specification of the types for which the class template is specialized. The types are used as a template argument and must be specified directly following the name of the class:
For these specializations, any definition of a member function must be defined as an "ordinary" member function, with each occurrence of T being replaced by the specialized type:
Class templates can be partially specialized. We can specify special implementations for particular circumstances, but the user must still define some template parameters. For example, for the following class template
The following example shows which template is used by which declaration:
If more than one partial specialization matches equally well, the declaration is ambiguous:
Function templates are special functions that can operate with generic types. It will allow us to create a function template whose functionality can be adapted to multiple types or classes without repeating the entire code for each type.
In C++, this can be achieved using template parameters. A template parameter is a special parameter that can be used to pass a type as an argument: just like regular function parameters can be used to pass values to a function, template parameters allow to pass also types to a function. These function templates can use these parameters like any other regular type.
The format for declaring function templates with type parameters is:
The only difference between both prototypes is using either the keyword class or the keyword typename. Its use is indistinct since both expressions have the same meaning and behave similarly.
For example, to create a template function that returns the greater one of two objects, we could use:
Here we have created a template function with myType as its template parameter. This template parameter represents a type that has yet to be specified, but that can be used in the template function as if it were a regular type. As you can see, the function template GetMax returns the greater of two parameters of this still undefined type. To use this function template, we use the following format for the function call:
function_name <type> (parameters);
For example, to call GetMax to compare two integer values of type int, we can write:
When the compiler encounters this call to a template function, it uses the template to automatically generate a function replacing each appearance of myType by the type passed as the actual template parameter (int in this case) and then calls it. The compiler automatically performs this process and is invisible to the programmer.
Here is the entire example:
In this case, we have used T as the template parameter name instead of myType because it is shorter and is a very common template parameter name. But you can use any identifier you like.
We used the function template GetMax() twice in the example above. The first time with arguments of type int and the second with arguments of type long. The compiler has instantiated and then called the appropriate version of the function each time.
As you can see, the type T is used within the GetMax() template function even to declare new objects of that type:
Therefore, the result will be an object of the same type as the parameters a and b when the function template is instantiated with a specific type.
In this specific case where the generic type T is used as a parameter for GetMax, the compiler can find out automatically which data type has to instantiate without explicitly specifying it within angle brackets (like we have done before specifying (<int> and <long>). So we could have written instead:
Since both i and j are of type int, the compiler can automatically determine that the template parameter can only be int. This implicit method produces the same result:
Notice how in this case, we called our function template GetMax() without explicitly specifying the type between angle-brackets <>. The compiler automatically determines what type is needed on each call.
Because our template function includes only one template parameter (class T) and the function template itself accepts two parameters, both of this T type, we cannot call our function template with two objects of different types as arguments:
It would not be correct since our GetMax function template expects two arguments of the same type, and in this call to it, we use objects of two different types.
We can also define function templates that accept more than one parameter type by specifying more template parameters between the angle brackets. For example:
In this case, our function template GetMin() accepts two parameters of different types and returns an object of the same type as the first parameter (T) that is passed. For example, after that declaration, we could call GetMin() with the following:
Even though j and l have different types since the compiler can determine the appropriate instantiation anyway.
We also can write class templates so that a class can have members that use template parameters as types. For example:
The class that we have just defined serves to store two elements of any valid type. For example, if we wanted to declare an object of this class to store two integer values of type int with the values 115 and 36, we would write:
mypair myobject (115, 36);
this same class would also be used to create an object to store any other type:
mypair myfloats (3.0, 2.18);
The only member function in the previous class template has been defined inline within the class declaration itself. In case we define a function member outside the declaration of the class template, we must always precede that definition with the template <...> prefix:
Notice the syntax of the definition of the member function getmax:
This declaration has three T's: The first is the template parameter. The second T refers to the type returned by the function. And the third T (the one between angle brackets) is also a requirement: It specifies that this function's template parameter is also the class template parameter.
If we want to define a different implementation for a template when a specific type is passed as a template parameter, we can declare a specialization of that template. For example, we have a very simple class called mycontainer that can store one element of any type, and it has just one member function called increase, which increases its value. But we find that when it stores an element of type char, it would be more convenient to have a completely different implementation with a function member uppercase, so we decide to declare a class template specialization for that type:
Below is the syntax used in the class template specialization:
First, notice that we precede the class template name with an empty template <> parameter list. It is to declare it as a template specialization explicitly.
But the specialization parameter after the class template name is more important than this prefix. This specialization parameter identifies the type for which we will declare a template class specialization (char). Notice the differences between the generic class template and the specialization:
The first line is the generic template, and the second one is the specialization. When we declare specializations for a template class, we must also define all its members, even those exactly equal to the generic template class, because there is no "inheritance" of members from the generic template to the specialization.