So far in this chapter, you have learned how to do generic programming with function templates and class templates. As you understand the class template, a single template class can support any built-in and user-defined data types. However, there are times when we need to treat certain data types with some special treatment with respect to the other data types. In such cases, C++ offers us explicit class specialization support to handle selective data types with differential treatment.
Consider the STL deque container; though deque looks fine for storing, let's say, string, int, double, and long, if we decide to use deque to store a bunch of boolean types, the bool data type takes at least one byte, while it may vary as per compiler vendor implementation. While a single bit can efficiently represent true or false, a boolean at least takes one byte, that is, 8 bits, and the remaining 7 bits are not used. This may appear as though it's okay; however, if you have to store a very large deque of booleans, it definitely doesn't appear to be an efficient idea, right? You may think, what's the big deal? We could write another specialized class or template class for bool. But this approach requires end users to use different classes for different data types explicitly, and this doesn't sound like a good design either, right? This is exactly where C++'s explicit class specialization comes in handy.
Never mind if you aren't convinced yet; the following example will help you understand the need for explicit class specialization and how explicit class specialization works.
Let us develop a DynamicArray class to support a dynamic array of any data type. Let's start with a class template, as shown in the following program:
#include <iostream>
#include <deque>
#include <algorithm>
#include <iterator>
using namespace std;
template < class T >
class DynamicArray {
private:
deque< T > dynamicArray;
typename deque< T >::iterator pos;
public:
DynamicArray() { initialize(); }
~DynamicArray() { }
void initialize() {
pos = dynamicArray.begin();
}
void appendValue( T element ) {
dynamicArray.push_back ( element );
}
bool hasNextValue() {
return ( pos != dynamicArray.end() );
}
T getValue() {
return *pos++;
}
};
The preceding DynamicArray template class internally makes use of the STL deque class. Hence, you could consider the DynamicArray template class a custom adapter container. Let's explore how the DynamicArray template class can be used in main.cpp with the following code snippet:
#include "dynamicarray.h"
#include "dynamicarrayforbool.h"
int main () {
DynamicArray<int> intArray;
intArray.appendValue( 100 );
intArray.appendValue( 200 );
intArray.appendValue( 300 );
intArray.appendValue( 400 );
intArray.initialize();
cout << "nInt DynamicArray values are ..." << endl;
while ( intArray.hasNextValue() )
cout << intArray.getValue() << "t";
cout << endl;
DynamicArray<char> charArray;
charArray.appendValue( 'H' );
charArray.appendValue( 'e' );
charArray.appendValue( 'l' );
charArray.appendValue( 'l' );
charArray.appendValue( 'o' );
charArray.initialize();
cout << "nChar DynamicArray values are ..." << endl;
while ( charArray.hasNextValue() )
cout << charArray.getValue() << "t";
cout << endl;
DynamicArray<bool> boolArray;
boolArray.appendValue ( true );
boolArray.appendValue ( false );
boolArray.appendValue ( true );
boolArray.appendValue ( false );
boolArray.initialize();
cout << "nBool DynamicArray values are ..." << endl;
while ( boolArray.hasNextValue() )
cout << boolArray.getValue() << "t";
cout << endl;
return 0;
}
Let's quickly compile the program using the following command:
g++ main.cpp -std=c++17
./a.out
The output is as follows:
Int DynamicArray values are ...
100 200 300 400
Char DynamicArray values are ...
H e l l o
Bool DynamicArray values are ...
1 0 1 0
Great! Our custom adapter container seems to work fine.