This lesson and the next are optional reading for those desiring a deeper knowledge of C++ templates. Partial template specialization is not used all that often (but can be useful in specific cases).
In lesson 19.2 -- Template non-type parameters, you learned how expression parameters could be used to parameterize template classes.
Let’s take another look at the Static Array class we used in one of our previous examples:
This class takes two template parameters, a type parameter, and an expression parameter.
Now, let’s say we wanted to write a function to print out the whole array. Although we could implement this as a member function, we’re going to do it as a non-member function instead because it will make the successive examples easier to follow.
Using templates, we might write something like this:
This would allow us to do the following:
and get the following result:
0 1 2 3
Although this works, it has a design flaw. Consider the following:
(We covered std::strcpy in lesson 11.6 -- C-style strings if you need a refresher)
This program will compile, execute, and produce the following value (or one similar):
H e l l o , w o r l d !
For non-char types, it makes sense to put a space between each array element, so they don’t run together. However, with a char type, it makes more sense to print everything run together as a C-style string, which our print() function doesn’t do.
So how can we fix this?
Template specialization to the rescue?
One might first think of using template specialization. The problem with full template specialization is that all template parameters must be explicitly defined.
Consider:
As you can see, we’ve now provided an overloaded print function for fully specialized StaticArray<char, 14>. Indeed, this prints:
Hello, world!
Although this solves the issue of making sure print() can be called with a StaticArray<char, 14>, it brings up another problem: using full template specialization means we have to explicitly define the length of the array this function will accept! Consider the following example:
Calling print() with char12 will call the version of print() that takes a StaticArray<T, size>, because char12 is of type StaticArray<char, 12>, and our overloaded print() will only be called when passed a StaticArray<char, 14>.
Although we could make a copy of print() that handles StaticArray<char, 12>, what happens when we want to call print() with an array size of 5, or 22? We’d have to copy the function for each different array size. That’s redundant.
Obviously full template specialization is too restrictive a solution here. The solution we are looking for is partial template specialization.
Partial template specialization
Partial template specialization allows us to specialize classes (but not individual functions!) where some, but not all, of the template parameters have been explicitly defined. For our challenge above, the ideal solution would be to have our overloaded print function work with StaticArray of type char, but leave the length expression parameter templated so it can vary as needed. Partial template specialization allows us to do just that!
Here’s our example with an overloaded print function that takes a partially specialized StaticArray:
As you can see here, we’ve explicitly declared that this function will only work for StaticArray of type char, but size is still a templated expression parameter, so it will work for char arrays of any size. That’s all there is to it!
Here’s a full program using this:
This prints:
Hello, world! Hello, mom!
Just as we expect.
Partial template specialization can only be used with classes, not template functions (functions must be fully specialized). Our void print(StaticArray<char, size> &array) example works because the print function is not partially specialized (it’s just an overloaded function using a class parameter that’s partially specialized).
Partial template specialization for member functions
The limitation on the partial specialization of functions can lead to some challenges when dealing with member functions. For example, what if we had defined StaticArray like this?
print() is now a member function of class StaticArray<T, int>. So what happens when we want to partially specialize print(), so that it works differently? You might try this:
Unfortunately, this doesn’t work, because we’re trying to partially specialize a function, which is disallowed.
So how do we get around this? One obvious way is to partially specialize the entire class:
This prints:
0 1 2 3 4 5 4.000000e+00 4.100000e+00 4.200000e+00 4.300000e+00
While it works, this isn’t a great solution, because we had to duplicate a lot of code from StaticArray<T, size> to StaticArray<double, size>.
If only there were some way to reuse the code in StaticArray<T, size> in StaticArray<double, size>. Sounds like a job for inheritance!
You might start off trying to write that code like this:
How do we reference StaticArray
Fortunately, there’s a workaround, by using a common base class:
This prints the same as above, but has significantly less duplicated code.