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.4 -- 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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
template <class T, int size> // size is the expression parameter class StaticArray { private: // The expression parameter controls the size of the array T m_array[size]{}; public: T* getArray() { return m_array; } T& operator[](int index) { return m_array[index]; } }; |
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:
1 2 3 4 5 6 |
template <typename T, int size> void print(StaticArray<T, size> &array) { for (int count{ 0 }; count < size; ++count) std::cout << array[count] << ' '; } |
This would allow us to do the following:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
#include <iostream> #include <cstring> template <class T, int size> // size is the expression parameter class StaticArray { private: // The expression parameter controls the size of the array T m_array[size]{}; public: T* getArray() { return m_array; } T& operator[](int index) { return m_array[index]; } }; template <typename T, int size> void print(StaticArray<T, size> &array) { for (int count{ 0 }; count < size; ++count) std::cout << array[count] << ' '; } int main() { // declare an int array StaticArray<int, 4> int4{}; int4[0] = 0; int4[1] = 1; int4[2] = 2; int4[3] = 3; // Print the array print(int4); return 0; } |
and get the following result:
0 1 2 3
Although this works, it has a design flaw. Consider the following:
1 2 3 4 5 6 7 8 9 10 11 12 |
int main() { // declare a char array StaticArray<char, 14> char14{}; std::strcpy(char14.getArray(), "Hello, world!"); // Print the array print(char14); return 0; } |
(We covered std::strcpy in lesson 9.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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
#include <iostream> #include <cstring> template <class T, int size> // size is the expression parameter class StaticArray { private: // The expression parameter controls the size of the array T m_array[size]{}; public: T* getArray() { return m_array; } T& operator[](int index) { return m_array[index]; } }; template <typename T, int size> void print(StaticArray<T, size> &array) { for (int count{ 0 }; count < size; ++count) std::cout << array[count] << ' '; } // Override print() for fully specialized StaticArray<char, 14> template <> void print(StaticArray<char, 14> &array) { for (int count{ 0 }; count < 14; ++count) std::cout << array[count]; } int main() { // declare a char array StaticArray<char, 14> char14{}; std::strcpy(char14.getArray(), "Hello, world!"); // Print the array print(char14); return 0; } |
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:
1 2 3 4 5 6 7 8 9 10 11 12 |
int main() { // declare a char array StaticArray<char, 12> char12{}; std::strcpy(char12.getArray(), "Hello, mom!"); // Print the array print(char12); return 0; } |
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:
1 2 3 4 5 6 7 |
// overload of print() function for partially specialized StaticArray<char, size> template <int size> // size is still a templated expression parameter void print(StaticArray<char, size> &array) // we're explicitly defining type char here { for (int count{ 0 }; count < size; ++count) std::cout << array[count]; } |
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
#include <iostream> #include <cstring> template <class T, int size> // size is the expression parameter class StaticArray { private: // The expression parameter controls the size of the array T m_array[size]{}; public: T* getArray() { return m_array; } T& operator[](int index) { return m_array[index]; } }; template <typename T, int size> void print(StaticArray<T, size> &array) { for (int count{ 0 }; count < size; ++count) std::cout << array[count] << ' '; } // overload of print() function for partially specialized StaticArray<char, size> template <int size> void print(StaticArray<char, size> &array) { for (int count{ 0 }; count < size; ++count) std::cout << array[count]; } int main() { // Declare an char array of size 14 StaticArray<char, 14> char14{}; std::strcpy(char14.getArray(), "Hello, world!"); // Print the array print(char14); // Now declare an char array of size 12 StaticArray<char, 12> char12{}; std::strcpy(char12.getArray(), "Hello, mom!"); // Print the array print(char12); return 0; } |
This prints:
Hello, world! Hello, mom!
Just as we expect.
Note that as of C++14, 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?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
template <class T, int size> // size is the expression parameter class StaticArray { private: // The expression parameter controls the size of the array T m_array[size]{}; public: T* getArray() { return m_array; } T& operator[](int index) { return m_array[index]; } void print() { for (int i{ 0 }; i < size; ++i) std::cout << m_array[i] << ' '; std::cout << '\n'; } }; |
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:
1 2 3 4 5 6 7 8 |
// Doesn't work template <int size> void StaticArray<double, size>::print() { for (int i{ 0 }; i < size; ++i) std::cout << std::scientific << m_array[i] << ' '; std::cout << '\n'; } |
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
#include<iostream> template <class T, int size> // size is the expression parameter class StaticArray { private: // The expression parameter controls the size of the array T m_array[size]{}; public: T* getArray() { return m_array; } T& operator[](int index) { return m_array[index]; } void print() { for (int i{ 0 }; i < size; ++i) std::cout << m_array[i] << ' '; std::cout << "\n"; } }; template <int size> // size is the expression parameter class StaticArray<double, size> { private: // The expression parameter controls the size of the array double m_array[size]{}; public: double* getArray() { return m_array; } double& operator[](int index) { return m_array[index]; } void print() { for (int i{ 0 }; i < size; ++i) std::cout << std::scientific << m_array[i] << ' '; std::cout << '\n'; } }; int main() { // declare an integer array with room for 6 integers StaticArray<int, 6> intArray{}; // Fill it up in order, then print it for (int count{ 0 }; count < 6; ++count) intArray[count] = count; intArray.print(); // declare a double buffer with room for 4 doubles StaticArray<double, 4> doubleArray{}; for (int count{ 0 }; count < 4; ++count) doubleArray[count] = (4.0 + 0.1 * count); doubleArray.print(); return 0; } |
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:
1 2 |
template <int size> // size is the expression parameter class StaticArray<double, size>: public StaticArray< // Then what? |
How do we reference StaticArray
Fortunately, there’s a workaround, by using a common base class:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
#include<iostream> template <class T, int size> // size is the expression parameter class StaticArray_Base { protected: // The expression parameter controls the size of the array T m_array[size]{}; public: T* getArray() { return m_array; } T& operator[](int index) { return m_array[index]; } void print() { for (int i{ 0 }; i < size; ++i) std::cout << m_array[i]; std::cout << '\n'; } virtual ~StaticArray_Base() = default; }; template <class T, int size> // size is the expression parameter class StaticArray: public StaticArray_Base<T, size> { public: }; template <int size> // size is the expression parameter class StaticArray<double, size>: public StaticArray_Base<double, size> { public: void print() { for (int i{ 0 }; i < size; ++i) std::cout << std::scientific << this->m_array[i] << ' '; // note: The this-> prefix in the above line is needed. // See https://stackoverflow.com/a/6592617 or https://isocpp.org/wiki/faq/templates#nondependent-name-lookup-members for more info on why. std::cout << '\n'; } }; int main() { // declare an integer array with room for 6 integers StaticArray<int, 6> intArray{}; // Fill it up in order, then print it for (int count{ 0 }; count < 6; ++count) intArray[count] = count; intArray.print(); // declare a double buffer with room for 4 doubles StaticArray<double, 4> doubleArray{}; for (int count{ 0 }; count < 4; ++count) doubleArray[count] = (4.0 + 0.1 * count); doubleArray.print(); return 0; } |
This prints the same as above, but has significantly less duplicated code.
![]() |
![]() |
![]() |
someone told me that when I'm doing an inheritance for template classes the derived class sends it's template parameters to the base class,but I dunno if he was right or not.he said something like this happens : using StaticArray=StaticArray_Base<T,size>.any Idea I really am confused..
sorry I accidentally used another comment instead of replay to my previous comment..
Hey,I'm very confused with something.how are you giving m_array a size using a child class template parameter ? how is that possible here ? in inheritance we couldn't initialize member variables of base class with a derived class. I'm tlking about this snippet:
would be thankful if you could clear me,thanks a lot.
This creates a new type. It doesn't matter where the `size` argument comes from.
The `using` alias you showed has nothing to do with this.
Hey ! Thanks for this great tutorial, i love it.
For the partial specialization of print function, i have a compile error :
Multiple definition of 'void print<char,14>(StaticArray<char,14>&)
I compile with code block, compiler gnu gcc compiler.
Do i have to check an option ?
One fact I find interesting is this. Maybe it's obvious but, while a partial specialization of a member function of a
templated class
doesn't work because it is interpreted as a partial specialization (forbidden) of function
.
Once you partially specialize the class into
(using the StaticArray_Base above), the same function definition as above is now interpreted as the definition (with its own repetition of template declaration) of the template function
, member of partially-specialized class
.
And therefore it's possible to write:
which looks identical to a partial function specialization as above, but it's not. I think this could be useful to appreciate the different interpretations, despite the same appearance.
Hi, in last example on partial template with member functions, why do we need virtual functions at all? wouldn't a simple overlad of print() function in
work as well? Is it maybe to make StaticArray_Base abstract (and thus non-instantiable)? Thanks.
Good catch. I don't know why Alex chose to make `print()` virtual. It would only be useful if `StaticArray_Base` was meant to be used directly, which it isn't. I made `print()` non-virtual, thanks!
I think line 49 under "Partial template specialization" should be,
Only strcpy_s accept destination length argument.
Also, in the last example, i am not able to understand how line 34 works.
Did StaticArray_Base<double, size> take a double template parameter because StaticArray_Base is a template class of type <T, size> ?
And why wouldn't the below code work ?
We should be able to do something like this right?
But i get this error for line 29:"a template argument list is not allowed in a declaration of a primary template." What does it mean ?
And as a result line 53 wont work too .
> strcpy
Fixed, thanks!
> Did StaticArray_Base take a double template parameter because StaticArray_Base is a template class of type ?` to use `double`s.
Yes, and we want the `StaticArray_Base` portion of `StaticArray
> We should be able to do something like this right?
No, that defeats the purpose of using a template. You're not using the first template parameter (`double`) for anything, remove it.
Thank you @nascardriver. I kinda get it but i wonder why it dosen't allow me to "declare a double buffer with room for 4 doubles" if i delete line 27-31 in the code of last example?
Is it because when you use inheritance with a common base for templates, only the Derived classes are used for any kind of operation and the Base class exist only to serve as a base for the derived classes ? If so then,
also must be using the class from line 27-31 for everything, except for printing using doubleArray.print(); which is done by the next derived class containing the override. Am i correct ?
nvm, ignore this. Obviously they are derived class objects. Stupid me :D
Is there any need for explicitly defining the constructor in the StaticArray<>?
In chapter 12 an entire lesson emphasises the need to make the destructors virtual if they're not protected. Why did StaticArray_Base<> violate this?
> Is there any need for explicitly defining the constructor in the StaticArray<>?
No. There was lesson that suggested always adding a constructor. That's not needed, constructor removed.
> Why did StaticArray_Base<> violate this?
I suppose Alex did pump up his warnings when he wrote this example. Fixed.
Thanks for your continuous feedback!
I'm glad to help!
"In chapter 12 an entire lesson emphasises the need to make the destructors virtual if they're not protected. Why did StaticArray_Base<> violate this?"
Going along with this, I believe 'StaticArray' and 'StaticArray<double, size>' should be made final, or given virtual (or protected) destructors.
In the inheritance example, `StaticArray` has a virtual destructor, because its parent has a virtual destructor (All children of a class with a virtual destructor have a virtual destructor). In the other examples, I agree those classes should use `final`. If we update just this lesson, people will ask why the classes are `final` here but not in the other lessons, so we'd have update all lessons after chapter 12. This takes a lot of time that's better spent in other places. I'm personally not using `final`, but I'll try starting to do it to see how it goes.
Maybe there is duplicated code but isn't readability more important so in my opinion the code:
is better than :
???
> Maybe there is duplicated code but isn't readability more important
No, duplicate code is terrible. It might not seem like that now, but imagine if that class has 100 member function. Maintaining such code is pretty much impossible.
The common base class barely harms the readability. The are solutions entirely without specialization (via type traits).
For some reason strcpy_s() is not working on Code::Block 17.12
I had to use strcpy to get the codes above working.
Is strcpy_s() only available in newer libraries?
It's only available in some libraries. Don't use it unless you verify that it's available.
Right! Thanks again!