In the previous lesson 19.5 -- Function template specialization, we saw how it was possible to specialize functions in order to provide different functionality for specific data types. As it turns out, it is not only possible to specialize functions, it is also possible to specialize an entire class!
Consider the case where you want to design a class that stores 8 objects. Here’s a simplified class to do so:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
template <class T> class Storage8 { private: T m_array[8]; public: void set(int index, const T &value) { m_array[index] = value; } const T& get(int index) const { return m_array[index]; } }; |
Because this class is templated, it will work fine for any given type:
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 |
#include <iostream> int main() { // Define a Storage8 for integers Storage8<int> intStorage; for (int count{ 0 }; count < 8; ++count) intStorage.set(count, count); for (int count{ 0 }; count < 8; ++count) std::cout << intStorage.get(count) << '\n'; // Define a Storage8 for bool Storage8<bool> boolStorage; for (int count{ 0 }; count < 8; ++count) boolStorage.set(count, count & 3); std::cout << std::boolalpha; for (int count{ 0 }; count<8; ++count) { std::cout << boolStorage.get(count) << '\n'; } return 0; } |
This example prints:
0 1 2 3 4 5 6 7 false true true true false true true true
While this class is completely functional, it turns out that the implementation of Storage8<bool> is much more inefficient than it needs to be. Because all variables must have an address, and the CPU can’t address anything smaller than a byte, all variables must be at least a byte in size. Consequently, a variable of type bool ends up using an entire byte even though technically it only needs a single bit to store its true or false value! Thus, a bool is 1 bit of useful information and 7 bits of wasted space. Our Storage8<bool> class, which contains 8 bools, is 1 byte worth of useful information and 7 bytes of wasted space.
As it turns out, using some basic bit logic, it’s possible to compress all 8 bools into a single byte, eliminating the wasted space altogether. However, in order to do this, we’ll need to revamp the class when used with type bool, replacing the array of 8 bools with a variable that is a single byte in size. While we could create an entirely new class to do so, this has one major downside: we have to give it a different name. Then the programmer has to remember that Storage8<T> is meant for non-bool types, whereas Storage8Bool (or whatever we name the new class) is meant for bools. That’s needless complexity we’d rather avoid. Fortunately, C++ provides us a better method: class template specialization.
Class template specialization
Class template specialization allows us to specialize a template class for a particular data type (or data types, if there are multiple template parameters). In this case, we’re going to use class template specialization to write a customized version of Storage8<bool> that will take precedence over the generic Storage8<T> class. This works analogously to how a specialized function takes precedence over a generic template function.
Class template specializations are treated as completely independent classes, even though they are allocated in the same way as the templated class. This means that we can change anything and everything about our specialization class, including the way it’s implemented and even the functions it makes public, just as if it were an independent class. Here’s our specialized 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 |
template <> // the following is a template class with no templated parameters class Storage8<bool> // we're specializing Storage8 for bool { // What follows is just standard class implementation details private: unsigned char m_data{}; public: void set(int index, bool value) { // Figure out which bit we're setting/unsetting // This will put a 1 in the bit we're interested in turning on/off auto mask{ 1 << index }; if (value) // If we're setting a bit m_data |= mask; // Use bitwise-or to turn that bit on else // if we're turning a bit off m_data &= ~mask; // bitwise-and the inverse mask to turn that bit off } bool get(int index) { // Figure out which bit we're getting auto mask{ 1 << index }; // bitwise-and to get the value of the bit we're interested in // Then implicit cast to boolean return (m_data & mask); } }; |
First, note that we start off with template<>
. The template keyword tells the compiler that what follows is templated, and the empty angle braces means that there aren’t any template parameters. In this case, there aren’t any template parameters because we’re replacing the only template parameter (T) with a specific type (bool).
Next, we add <bool>
to the class name to denote that we’re specializing a bool version of class Storage8.
All of the other changes are just class implementation details. You do not need to understand how the bit-logic works in order to use the class (though you can review O.2 -- Bitwise operators if you want to figure it out, but need a refresher on how bitwise operators work).
Note that this specialization class utilizes a single unsigned char (1 byte) instead of an array of 8 bools (8 bytes).
Now, when we declare a class of type Storage8<T>, where T is not a bool, we’ll get a version stenciled from the generic templated Storage8<T> class. When we declare a class of type Storage8<bool>, we’ll get the specialized version we just created. Note that we have kept the publicly exposed interface of both classes the same -- while C++ gives us free reign to add, remove, or change functions of Storage8<bool> as we see fit, keeping a consistent interface means the programmer can use either class in exactly the same manner.
We can use the exact same example as before to show both Storage8<T> and Storage8<bool> being instantiated:
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 |
int main() { // Define a Storage8 for integers (instantiates Storage8<T>, where T = int) Storage8<int> intStorage; for (int count{ 0 }; count < 8; ++count) { intStorage.set(count, count); } for (int count{ 0 }; count<8; ++count) { std::cout << intStorage.get(count) << '\n'; } // Define a Storage8 for bool (instantiates Storage8<bool> specialization) Storage8<bool> boolStorage; for (int count{ 0 }; count < 8; ++count) { boolStorage.set(count, count & 3); } std::cout << std::boolalpha; for (int count{ 0 }; count < 8; ++count) { std::cout << boolStorage.get(count) << '\n'; } return 0; } |
As you might expect, this prints the same result as the previous example that used the non-specialized version of Storage8<bool>:
0 1 2 3 4 5 6 7 false true true true false true true true
It’s worth noting again that keeping the public interface between your template class and all of the specializations identical is generally a good idea, as it makes them easier to use -- however, it’s not strictly necessary.
![]() |
![]() |
![]() |
thanks for your tutorial, I get so many knowledge. I am Chinese, do you allow me to translate this tutorial. I think more people will like this tutorial. I will attach your link in every chapter. Thank you again.
can we define the member function set for bool template parameter as the following so we don't have to have the else part, or is it wrong?
>>In this case, there aren’t any template parameters because we’re replacing the only template parameter (typename T) with a specific type (bool).
Shouldn't inside the parentheses be " only template parameter (class T) with a ..."?
Hello Nascardriver,
I wrote this as an alternative way to having class template specialization for having bool type.
is that correct?
Yes it is. You don't need `resetBit`, `bitset::set` has an optional second parameter, you can call
Hi,
Shouldn't we static_cast<unsigned char> the bitmask since it's int literal?
The type didn't matter, I updated the examples to use `auto`.
Hello, on line 31 of the bool class specialization, can it simply be: "return (m_data & mask);"?
Do we need the "!= 0" part on there? I'm a bit confused because I thought that anything that's not 0000 0000 would be implicitly converted to "true" anyway...
Thanks, as always!
Hi Jon!
You can omit to "!= 0". Implicit casts can be unwanted at times, so it's good to avoid them.
Lesson updated. Thanks for pointing out the redundancy.
Hi Alex,
I have a question about Class template specialization : In fact I was reading a book (complete Guide ...) and I ended up with this remark:
[Begin of Paste]
...
If you specialize a class template, you must also specialize all member functions.
Although it is possible to specialize a single member function of a class template,
once you have done so, you can no longer specialize the whole class template
instance that the specialized member belongs to.
[End of Paste]
So Here what does it mean by the "Once you have ...." I couldn't understand it.
Thanks in advance.
Hi Aymen!
Suppose we have this class
This isn't specialized, it will behave the same for all types.
If we wanted to specialize @fn, there are two options. One being to specialize the entire class
If we now run
we'll get
The other option is specializing only @fn, without specializing the entire class
Running the code above prints
What your book is saying is that once you've used the second option (specializing only @fn), you can no longer use option 1 (specializing the entire class).
It seems like this requires a lot of duplication of code. Is it possible in C++ to limit code duplication for specialized class templates by re-using code of the generic class template? Like if the Storage8<T> class template had a print() method that didn't require special handling for booleans, could you use that version natively in the Storage8<bool> class template (i.e. without having to copy that code into your specialized template)?
I think the rest of the chapter answers this question. Basically, use a base-class template, then use partial specialization for the functionality you need to change, right?
Yep.
Can someone explain me why i have to change everything to const?
This version is working, but i don't understand why...
With the non-const version this code would be legal
, because @getMember returns a non-const reference. But it shouldn't be legal, because @s is const, hence the error.
@setMember could do member = 8, because @member is non-const. But you're passing the literal 5, which cannot be changed.
Thanks, can you also help me why the following code isn't working?
Remove line 24. You only need that before the class declaration.
Thanks, now it's working.
But can you inform me about why?
Appreciate your work!
You need it in line 16 to tell the compiler that @Storage<bool> is a template class, but without template parameters. When defining @Storage::Storage, this is not a question, because the function itself is not templated, just the class is and you already told the compiler about the class.
That's how I think about it.
Ok, ty
Hi Alex
I want to restrict the templates for specific types
I want the above class to work only for string and wstring and doesnot want to allow any of the other datatypes
Can you please let me know how can we do this
Thanks for all the wonderfull training site you have created it is alwat useful to go through this
Regards,
Mehul shah
Hi Mehul!
C++11 introduced type traits. It takes some time to get behind them but they're useful.
References
* http://www.cplusplus.com/reference/type_traits/
Thanks .. It was really usefull
Hi Alex,
is there a way to define the long functions of a specialised template class outside of the class itself?
P.S.: Thank you very much for this great tutorial!
Yes, absolutely. Here's how we'd declare Storage8::set outside the class:
Thank you very much Alex!
When i tried to do this on my own, I made the following mistake:
Could you explain why this doesn't work even so the "template<>" line is needed for the specialisation of function templates and also the outside declaration of a normal template classes member functions?
(I got the error message: "error: template-id 'someClass<>' for 'someClass<bool>::someClass()' does not match any template declaration | note: candidates are: someClass<bool>::someClass()"
I haven't been able to find a good description of why template<> isn't used in the case where we're defining a member of an explicitly specialized class template. I initially put it in as well, but took it out when the compiler complained about it. Maybe one of the other readers of this page will be able to enlighten both of us.
Hi Alex,
Thanks for really nice lessons. Can you please explicitly explain the bool specialized class template code and its execution in main function. Even with the comments it is not clear. I don't understand the execution logic of this code.
I specifically don't understand [boolStorage.set(count, count & 3);]. Why is there a 3?
Please see the lesson 3.8a on bit flags -- it covers the basics needed to understand how the bool specialized version works. The count & 3 is there just to initialize the boolStorage with interesting values, so we can prove it works.
Typo Spotted ! 'G' of "Get" must be in lower-case !
Fixed, thanks!
what you mean by publicly exposed interface?
and also what this line does?
By "publicly exposed interface", I mean a public function in the class.
In this context, this does a bitwise left-shift on the number 1 to create a bit mask that represents the bit we want. For example, if index is 0, this produces binary 0000 0001. If index is 1, this produces binary 0000 0010.
in this example i am not able to understand how this m_data |= mask; operation is possible without initialising m_data
Mistake on my part. I've added a constructor to initialize m_data to 0.
Again, amazing tutorial, and I'm glad that I made it this far, BUT I need clarification :
1- what does the template parameter declaration before the member functions (outside the class) do ?
2- how can I override ONLY ONE template parameter of a class that has multiple template parameters ?
And thank's (No really thank's :) )
1) It tells the compiler that this is a template function, so it knows what the template parameters are (otherwise it would flag them as undefined identifiers).
2) See the next lesson.
Awesome alex.. very helpful.... !!!!!!!!!
Hi Alex,
I have a question. In 14.3, you have defined a template class and there is a member function to override the operator []. Why we do not need it here in this lesson? Thanks.
There was a mistake in the example where I'd used operator[] without defining it. Instead of defining operator[] as part of the class, I've changed the example to use Set() and Get(). Either way is fine.
Hi,
I have a doubt.
Can we specialize a template class for a templated class?
template
class Storage8<MyClass>
{
---
---
};
Here MyClass is a templated class. Is the above valied? Where do we have to mention
template for MyClass?
Yes. There's nothing special about the fundamental data types in this context.
Hi! I like this Tutorial, It is very good. I did not understand line 13 in section Class template specialization
unsigned char nMask = 1 << nIndex; why does it need, <<nIndex?
I am looking compiler for c++ to run these program, from where I can get it? Please reply, Thank you.
integer 1 has boolean representation 0000 0001. operator<< in this context is a bitwise left shift. So we're shifting all of the bits left by the value of index. If index is 1, we shift left once, which leaves us with 0000 0010. If index is 2, we shift left twice, which leaves us with 0000 0100. We can then use this as a bit mask to select only that bit.
Hi,
I am really enjoying this readings.
i have a clarification here.
Is this legal to the compiler ?
if so why the following gives compiler error.
cIntStorage[nCount] will work if operator[] has been overridden for the Storage8 class.
Similarly, ob[i] will work if operator[] has been overridden for class A.
Which you didn't.
This lesson is not about overloading the operator[]. You can read on lesson 9.8 and 14.3 for examples on how to do it.
Hi,
I don't understand the meaning of line 15 of the first example in this page(in the main() function), I mean:
cBoolStorage.Set(nCount, nCount & 3);
I have problem with the second argument of the Set function? Would you please explain it more?(I know it should be a bool, but how could it be?)
Herewith, I would like to thank the writer of this tutorial! I always enjoy reading these pages!
I just realized I made a mistake in my code and flipped the true/false results. That's been fixed, so take another look now. nCount & 3 produces an integer value. C++ will implicitly cast the result of this expression to an boolean. If the result is 0, the boolean will be false. If the result is non-zero, the boolean will be true. So when is nCount & 3 == 0? When nCount is a number that has it's first and second bits set to 0 (in other words, 0, 4, 8, 12, ...). Consequently, when nCount is one of these numbers, nCount & 3 will be zero, and the boolean will be set to false. When nCount is not one of these numbers, nCount & 3 will be non-zero, which means the boolean will be set to true.
Thank you for explaining this fact, Alex! This whole tutorial is awesome!
Seconded.
Thanks for the explanation