In the first section of the book, we have learned about the basic syntax and usage of a function template. In this second part of the book, we will focus on the class templates. Class templates are an essential tool for the programmer to design classes that can work on the generic data type. While class templates are an excellent tool for the programmers, it is slightly complex than the function templates. The syntax and usage may look a bit daunting for new programmers. In this chapter, we will learn about how to declare and define classes with templated parameters step by step with simple examples at each step.
In this chapter, we will cover the following topics:
- Introduction to class templates
- Learning about custom non-template storage class
- Learning about STL storage class vector
- Syntax of the class template declaration
- Defining the member function of a class template
- Writing the copy constructor of a class template
- Writing the copy assignment operator of a class template
- Creating an instance of a class template
- Learning about Function Objects
Technical requirements
Most of the example in this chapter will work with standard C++11 compiler. Some of the examples in this chapter may require support of C++14 or higher compiler.
The code files for this chapter can be found on GitHub at Source Code.
Introduction to class templates
So far, we have seen many examples that evolved around template functions in the previous chapters. However, templates are not restricted to function only. You can write a class with template type parameters as well. A class with template type parameters is called a class template. Class templates enable programmers to write generic programs that can operate on different data types. Class templates act as the container of data or data storage.
In this part of the book, we will dive into the details of class templates. We will start with the basic understanding of class template syntax and then gradually examine other aspects of it.
Learning about custom non-template storage class
As mentioned before, template classes act as data store. But what is a data store? To understand that let us first declare a rudimentary data store class ourselves as shown in the following listing:
#include <iostream>
#include <iomanip>
class MyStore {
private:
int *store;
size_t len;
size_t curr_len;
public:
explicit MyStore(size_t);
void add_element(int);
double average();
~MyStore();
};
As you can see, we have declared a class MyStore with a pointer type member variable, store that points to int type data. The class also has counters to keep track of the total capacity of the store, the len, and the current size of the store, curr_len. The member variable curr_len is initialized to 0 in the constructor and is incremented every time a new element is added. The class provides two public methods add_elements() to add new elements to the store and another average() method, which returns the average of all the elements in the store.
Now, let us define the member functions of the class. The class MyStore has a single parameter constructor which takes the initial size of the store as the input parameter at the time of object creation. In the single parameter constructor, we allocate the initial memory required for the store using malloc(). We allocate the memory area which can hold len number of int type data. The following is the single parameter constructor for the class:
MyStore::MyStore(size_t len) : len(len), curr_len(0)
{
store = new int[len];
}
In the destructor we have to do the opposite, i.e. we need to deallocate the memory area using malloc() library call in the constructor. The destructor code looks like the following:
MyStore::~MyStore()
{
delete [] store;
}
The class MyStore provides a member function add_element() for adding new elements to the store. The add_element() member function is defined as the following:
void MyStore::add_element(int elem)
{
if(curr_len < len) {
store[curr_len++] = elem;
} else {
std::cout << "Number of elements added exceeds the allocated size" << std::endl;
}
}
In the member function add_element(), we check if the curr_len is within the store’s limit. The member variable len keeps the current size of the store. If we try to add more elements to the store than the store size, a message is printed on the console informing the store size exceeded the maximum allowed size. The MyStore class also provides a member function average() which returns the average of all the elements in the store. The average() member function is defined as the following:
double MyStore::average()
{
auto avg = 0.0;
for(size_t index = 0; index < curr_len; ++index)
avg += store[index];
avg = (double)avg / curr_len;
return avg;
}
Finally, in the main() function, we have created an object of the class template MyStore by calling the single parameter constructor. The main() function is defined as the following:
int main()
{
MyStore s(5); // store created the for 5 elements
s.add_element(10);
s.add_element(30);
s.add_element(5);
s.add_element(22);
s.add_element(1);
s.add_element(50); // 6th element added
std::cout << std::fixed
<< std::setprecision(2)
<< s.average()
<< std::endl;
return 0;
}
In the main() function, when we pass the value of 5 to the single parameter constructor, a memory area large enough to accommodate 5 int type data is dynamically allocated by the malloc() library function. The memory allocation happens in the following line of code in the main():
store = new int[len];
After the allocation, the pointer store points to a memory area which can fit 5 int type data. The member variable curr_len holds the current size of the store. Notice that we have called the add_element() function 6 times with different data. That means we are trying to add more elements than the initial size of the store. Hence in the 6th call to add_element() the store size will be increased by another 100 locations by the logic implemented in the add_element() method. If you compile and run the program, you will see the following output:
$ g++ custom_storage_class.cpp -o custom_storage_class -Wall -Wextra -Wpedantic -Werror
VBR09@mercury:~/CPP-Templates-Up-and-Running/Chapter_6$ ./custom_storage_class
Maximum store size exceeded.
13.60
So, we have a storage class, MyStore, which can accommodate int type data on request. We have created a storage class with a fixed size to store data. The class also provides a member function that calculates the average of all the data elements and returns the same. The member functions are representative of any other possible functionality that a class of this type may have. You can extend this to fit your purpose.
However, what happens if we want to change the data type of the store? The only option we have is to write another class with the new data type in the custom implementation. In the next section, we will see how the STL-provided template container classes can be beneficial in that scenario.
Learning about STL storage class vector
Now that we have implemented a custom storage class, let us look at how we can achieve the same using STL storage class or containers. One of the most popular STL containers is std::vector. Let us try to understand how STL vectors can help us in achieving similar goal. Let us have a look at the following code listing:
#include <iostream>
#include <vector>
#include <iomanip>
int main()
{
std::vector<int> my_vector_store(5);
my_vector_store.push_back(10);
my_vector_store.push_back(30);
my_vector_store.push_back(5);
my_vector_store.push_back(22);
my_vector_store.push_back(1);
my_vector_store.push_back(50);
auto avg = 0.0;
for(auto itr : my_vector_store)
avg = avg + itr;
std::cout << std::fixed
<< std::setprecision(2)
<< (double)avg/6
<< std::endl;
return 0;
}
In this program, we have declared an STL vector container my_vector_store with type int, which means it can hold data of type int. The vector container class provides a member function push_back() to add new data to the container’s back. As the elements are added to the vector container, their size keeps on increasing. The internal mechanism of the vector container takes care of allocating memory space for the new elements.
Using std::vector, we can write clean code as most of the mechanisms of allocating and reallocating the memory space for the new elements are internalized by the vector class implementation. However, the main advantage is we can write generic code using vectors. Let us say we want to have a container for the double data type. If we use the custom implementation, we will have to rewrite the whole implementation again for the double data type. If we use a vector, we can declare another vector with data type double. That is possible because the vector is a template class. Go give you a bit of an idea how a vector class definition may look like, let’s have a look at the following skeleton of a vector class definition:
template<typename _Tp, typename _Allocator = std::allocator<_Tp> >
class vector
{
… // implementation details
…
}
Looking at the keyword template, you must have guessed this is to do with templates. Yes, vectors are special template classes provided in the STL. In the next section, we will learn about the basic syntax of a template class.
Syntax of Class template declaration
The syntax of a class template is more complex than the function templates. We will try to learn it by breaking it down and analyzing each part in steps. First, let us learn about declaration of a template class in the following section.
The declaration of a template class starts with the keyword template, followed by an opening angle bracket, and then the keyword typename to denote the type parameter and finally, the name of the type parameter and the closing angle bracket. T is used widely as the type parameter name, but you can use any other name if you would like. Like function templates, you can have multiple type parameters, each of which must be declared using the keyword typename or class. In Fig 6.1 we have outlined the basic syntax of a class template:

Figure 6.1 – Syntax of class template declaration
Following the class template declaration template, let us modify the previously written custom storage class declaration and turn it into a template class declaration as the following:
template<typename T>
class MyStore {
private:
T *store;
size_t len;
size_t curr_len;
public:
explicit MyStore(size_t);
void add_element(T);
double average();
~MyStore();
};
As you can see, the template class declaration starts with the keyword template, which is then followed by an angle bracket and the keyword typename. As we have seen earlier in the function template declaration, the keyword typename is used to declare a type parameter T in this case. The rules for the type parameter(s) remain the same for class templates as we have seen in function templates. Once we have declared all the type parameters (T in our case), we close the parameter declaration list using a closing angle bracket.
Inside the class declaration, we can use T for specifying the type of any variable or type of the member function parameters as we need. In this example, we have declared a pointer member variable store of type T. As before, T is a placeholder for any possible data types. Similarly, while declaring the member function add_element(), we have used type parameter T to declare the type of the function parameter(s). The member function add_element() takes a single parameter of type T. This completes the template class declaration. Now, we need to define the member functions of the template class. In the next section, we will learn how to define the member functions of a class template.
Defining the member functions of a class template
Defining the member functions of a class template is a little clumsy, but it is not difficult. The following is the syntax of defining the member function of a class template:

Figure 6.2 – Syntax of class template declaration
Let us define the member functions one by one. The first in the list is the single parameter constructor. Let us define the single parameter constructor as the following:
template<typename T>
MyStore<T>::MyStore(size_t len) : len(len), curr_len(0)
{
store = new T[len];
}
While defining the member functions, we must use the template<typename T> expression before every member function. Also, the member function name must be preceded by the class name and the type parameter within angular braces.
The MyStore<T> template class’s single parameter constructor is preceded by the class name MyStore along with the type parameter T within the opening and closing angle brackets. This will be the same for all other member functions’ definitions as well. Next, we will define the destructor as the following:
template<typename T>
MyStore<T>::~MyStore()
{
delete [] store;
}
As we have mentioned, the destructor ~MyStore() is preceded by the class type, which is MyStore<T>. In this case, even if the destructor does not use the type parameter T, we must specify the template class type. If we do not use the class type, we will get an error like the following:
invalid use of template-name 'MyStore' without an argument list
We will next define the add_element() member function as the following:
template<typename T>
void MyStore<T>::add_element(T elem)
{
if(curr_len < len) {
store[curr_len++] = elem;
} else {
std::cout << "Maximum size of the store reached." << std::endl;
}
}
The definition starts with the keyword template and then the type parameter list declaration using the keyword typename within the angle brackets. The we specify the return type of the member function which is void in this case. Then class type MyStore<T> followed by the scope resolution operator (::) precedes the member function name add_element(). The add_element() has a function parameter elem which is of type T. The function parameter elem represents a new element to be added to the store. If there is enough space in the store already, then the new element is added to the store, and the curr_len is incremented by one. If there is no more space left, then an error message is printed on the console saying the maximum size of the store has reached.
The last member function average() is defined as follows:
template<typename T>
double MyStore<T>::average()
{
auto avg = 0.0;
for(size_t index = 0; index < curr_len; ++index)
avg += store[index];
avg = (double)avg / curr_len;
return avg;
}
This member function average() does not use the type parameter T inside the function body. However, we have to start the definition using the keyword template, and then the type parameter list, the name of the member function average() has to be preceded by the class type MyStore<T>. Next, we will learn how to define a vital member of a class template which is the copy constructor.
Writing copy constructor of a class template
The copy constructor is used when we initialize an object of the class at the time of its creation using another existing object of that class. Let us say we have a class A, and we have created an instance of the class A named ‘a’. Now, let us say we are creating another instance of class A which is called ‘b’ as the following:
A a;
A b = a;
This is a valid expression for initializing a class object at the time of its creation from an existing object of the same class. The compiler internally invokes the copy constructor of the class to initialize the new object. By default, the copy constructor performs shallow copy, which is not suitable for classes with pointer-type data members. Hence, often we define the custom copy constructor of user defined classes to support the deep copy. We cannot go into the details of shallow and deep copy here as that is out of the scope of this book.
Writing the custom copy constructor of a template class is a bit complex but not difficult. Let us now first declare and then define the copy constructor of the MyStore<T> template class as described in the following sections.
Declaring the copy constructor of a template class
In the public section of the class, we declare the copy constructor like the following:
public:
…
MyStore<T>(const MyStore<T>&);
…
As you can see, the copy constructor has the same name as the class, followed by the template argument in angle brackets. Then we have specified the function parameters for the copy constructor, which is a const type reference to the MyStore<T> type class. Interestingly, if we drop the template type argument in the copy constructor declaration, it would be fine. So, we can declare the copy constructor like the following as well:
public:
…
MyStore(const MyStore&);
…
In the next section, we will see how we can define the copy constructor for the MyStore<T> template class.
Defining the copy constructor
In the copy constructor body, we must ensure that the pointer store is not just copied from one object to the other. Instead we need to first allocate memory for the store pointer with the right size memory and then copy the bytes (of all the elements) from the other object into the current object. Let us see how we can do that in the following code:
template<typename T>
MyStore<T>::MyStore(const MyStore<T>& other)
{
len = other.len;
curr_len = other.curr_len;
store = (T *) malloc(sizeof(T) * len);
memset(store, 0, sizeof(T) * len);
memcpy(store, other.store, len);
}
The definition of the copy constructor starts with the keyword template followed by the template type parameter declaration. Then we specify the template class MyStore<T>, followed by the scope resolution operator indicating the copy constructor is a member of the template class MyStore<T>. Then we specify the copy constructor’s name, which is the same as the class name MyStore. The parameter to the copy constructor needs special attention. A copy constructor’s parameter must be passed by reference and not pass by value. Pass by value would require the input object to be copied into the function parameter, which would require another copy constructor, and soon it will get into a spiral copy operation leading to stack overflow. Also, notice the const qualifier to ensure the copy constructor does not modify the object from which it is copying. In the copy constructor body, we are copying the length of the store from the input object to the current object. Then the memory is allocated for the required number of elements (len number of elements) using the malloc() library function. Finally, the content of the memory pointed to by the store pointer of the input object is copied into the memory pointed by the store pointer of the current object.
In the next section, we will learn how to declare and define the copy assignment operator.
Writing copy assignment operator of a class template
The copy assignment operator is used when we first create a new object of a class. Once the object is created then assign the content of an existing object of the same class into the newly created object.
The difference of copy constructor and assignment operator is that in the case of copy constructor, the new object is created and assigned the content of an existing object at the same time, but in the case of the copy assignment operator, we first create the object and then assign the content of an existing object into it. So, in the case of the copy assignment operator, the creation and assignment happen in two separate stages. Unlike the copy constructor, the copy assignment operator has a return value. Let us now see how to declare the copy assignment operator for the template class in the next section.
Declaring copy assignment operator of a template class
The copy assignment operator’s declaration goes in the public section of the class template MyStore<T>. Let us see how we do that in the following code:
public:
…
MyStore<T>& operator=(const MyStore<T>&);
…
As you can see in the copy assignment operator’s declaration, we first declare the return type. The return type of the copy assignment operator is a non-constant reference of the class template. The return type is followed by the keyword operator and then the assignment operator (=) itself. The argument list is specified within brackets, like a function. The copy assignment operator has only one argument, a const type reference of the template class. Next, we have to define the copy assignment operator. In the next section, we will see how we can define the copy assignment operator.
Defining copy assignment operator of a class template
Defining the copy assignment operator of the template class MyStore<T> begins with the keyword template followed by the template type parameter specification. Imagine the copy assignment operator as a member function whose name consists of the keyword operator followed by the assignment operator (=) itself and the return value is a reference to the template class i.e. MyStore<T>&. Let us see the definition of the copy assignment operator in the following code listing:
template<typename T>
MyStore<T>& MyStore<T>::operator=(const MyStore<T>& other)
{
if( this != &other) {
if(!store) {
len = other.len;
curr_len = other.curr_len;
store = new T[len];
memset(store, 0, sizeof(T) * len);
memcpy(store, other.store, len);
} else {
std::cout << "Store not empty!" << std::endl;
}
}
return *this;
}
Inside the copy assignment operator body, we first check if the input object is the same as the current object by comparing their addresses, ensure we are not copying the same object into itself. Then we also check if the store pointer is already initialized. If it is then print an error message reporting that the object is not empty and return. If the store pointer is not yet initialized, we assign the value of the len member of the input object into the current object’s len member variable. Similarly, we copy the input object’s current size (curr_len) into the current object’s current length member variable. Then we allocate memory for len number of elements with call to malloc(). Generally, if you are allocating heap memory (dynamically allocated memory comes from the heap), it may contain garbage. So it is a good practice to clear the memory with memset() library function call. Finally, we copy the elements from the input object into the current object using memcpy() library function. Once everything is done, we return the content of this pointer. The this pointer is a special pointer which always points to the current object. As we have now defined all the class member functions, it is time to understand how we use this class in a CPP program. In the next section, we will see how to instantiate a template class.
Creating an instance of a class template
Creating an instance of a template class is simple. We have to specify the name of the class template followed by the type (specified within angle brackets) the template class to be instantiated and then the new object’s name. The following is the syntax of template class instantiation:

Figure 6.3 – Syntax of class template declaration
In our present case, we can instantiate an object of the MyStore<T> template class as the following:
MyStore s(5); // store created the for 5 elements
This will tell the compiler to create an object of the MyStore<T> template class with an initial store size of 5. The passing of the parameter to the constructor is implementation dependent. In this case, we need to specifically pass a value to the single parameter constructor as the store’s initial size. Whether we pass any value in the constructor or what we pass in the constructor is class implementation-specific and will vary case to case basis.
Putting it all together
We have discussed all the various bits of a whole program. In this section we will put all the different bits into one place and put them into action. We will separate the template class implementation into a header file. The template code organization has been discussed in Chapter 5, Miscellaneous Template aspects. First we will declare the template class MyStore<T> in a header file as shown in the following listing:
#ifndef __MYSTORE_TEMPLATE_CLASS__
#define __MYSTORE_TEMPLATE_CLASS__
#include <iostream>
#include <iomanip>
#include <iostream>
#include <iomanip>
#include <string.h>
template<typename T>
class MyStore {
private:
T *store;
size_t len;
size_t curr_len;
public:
MyStore();
explicit MyStore(size_t);
MyStore<T>(const MyStore<T>&);
MyStore<T>& operator =(const MyStore<T>&);
void add_element(T);
double average();
~MyStore();
};
The declaration of a template class outlines the data which are being encapsulated within the class and the interfaces to access the data. With the template mechanism, we generalize the data type to instantiate the class for different data types. We also specify the access specifiers of the data and the member functions, that is, whether they are public or private, or protected. Once the class declaration is in place, the next task is to define the member functions. It is hard to fit in the entire file in this book. For the complete definition of the member functions please look into the GitHub link:
Once we have defined the template class, we are ready to use the template class in a program. In the following code listing, we have shown a cpp file which creates an object of the template class MyStore<T> and then calls various member function of it throughout the program. The header file is included in the main program using the #include preprocessor directive. Inside the main() function, we have created an instance of the MyStore<T> template class with an initial store size of 5 and the type specified for template class instantiation as int within angle brackets. Then by calling the add_element() member function we have then added some random values like 10, 30, 5, 22, 1 and 50. Now, let us have a look at the program:
template_class_implementation.cpp
#include “MyStore_template_class.h”
int main()
{
MyStore<int> s(5); // store created the for 5 elements
s.add_element(10);
s.add_element(30);
s.add_element(5);
s.add_element(22);
s.add_element(1);
s.add_element(50); // 6th element added
std::cout << std::fixed << std::setprecision(2) << s.average() << std::endl;
MyStore<int> s2 = s;
std::cout << std::fixed << std::setprecision(2) << s2.average() << std::endl;
MyStore<int> s3;
s3 = s2;
std::cout << std::fixed << std::setprecision(2) << s3.average() << std::endl;
return 0;
}
You will notice we have deliberately added more elements (a total of 6 elements) than the original store size (the original size was 5). While adding a new element, the add_element() function checks the current size of the store, tracked by the member variable curr_len. If the number of elements exceeds the store’s storage capacity, hold by the len member variable, an error message informing the store capacity has reached is printed on the console.
if(curr_len < len) {
store[curr_len++] = elem;
} else {
std::cout << "Maximum size of the store reached." << std::endl;
}
If you compile and run the program, it will give you the output like the following:
$ g++ template_class_implementation.cpp -o template_class_implementation -Wall -Wextra -Wpedantic -Werror
VBR09@mercury:~/CPP-Templates-Up-and-Running/Chapter_6$ ./template_class_implementation
Maximum size of the store reached.
13.60
8.00
8.00
If we want to change the datastore type to double, we have to create another instance of the MyStore<T> template class with the type double. Let us have a look at the following program to see how we can do that:
template_class_implementation_double.cpp
#include "MyStore_template_class.h"
int main()
{
MyStore<double> s(5); // store created the for 5 elements
s.add_element(23.5);
s.add_element(50.1);
s.add_element(10.4);
s.add_element(12.3);
s.add_element(5.7);
s.add_element(34.8); // 6th element added
std::cout << std::fixed << std::setprecision(2) << s.average() << std::endl;
return 0;
}
In this program, we have created an instance of the MyStore<T> template class with the double data type specified at the time of object creation within the angle brackets. This tells the compiler that the substitution of T has to be double. Anywhere T has been used in the class is replaced with the type double. Then we have added double type data to the instance using the add_element() member function. Similarly, we can use other data types to create instances of MyStore<T> template class and use it in our program. So, this is how we can write generic classes using the template mechanism which can work with different data types. In the next section, we will learn about the function objects.
Learning about function objects
Function objects are function-like objects of a class. By function-like we mean function objects are callable, at least they look like that. In a program, function objects are called the same way as functions are called. To understand this better let us have a look at the following program:
#ifndef __LOGGER_TEMPLATE_CLASS__
#define __LOGGER_TEMPLATE_CLASS__
#include <iostream>
#include <iomanip>
template<typename T>
class Logger {
public:
Logger() = default;
void operator () (const T&);
~Logger() = default;
};
template<typename T>
void Logger<T>::operator()(const T& val)
{
std::cout << __FILE__ << ":" << __LINE__ << " " << val << std::endl;
}
#endif
As you can see we have defined the template class Logger<T> with a template type parameter T in the header file called Logger_template_class.h. Inside the class body, we have declared a public function operator () which takes a const reference of type T. The function operator () takes a parameter of type T, adds the source file name and the line number, and then prints it on the console. The object of the Logger<T> template class can be used to trace the program flow for debugging purposes. In the following code listing we will write a .cpp file which will use our Logger<T> template class for logging purposes.
#include <iostream>
#include <iomanip>
#include "Logger_template_class.h"
int main()
{
Logger<int> logger_i;
logger_i(10);
Logger<double> logger_d;
logger_d(50.9);
Logger<const char *> logger_char;
logger_char("John Grisham");
Logger<std::string> logger_s;
logger_s("Hello World");
return 0;
}
The main() function is defined in the file function_object.cpp. In this file, we include the header file. Once we include the header file, the compiler pastes the template class definition into the cpp file. In the main() function, we first created an object of the Logger<T> template class with type int. Then we called that object with an int type value, just like a function call as the following:
logger_i(10);
Similarly, we did the same for the double, c-style string (char *) and std::string type data. If we compile and run the program, the output will look like the following:
$ g++ function_object.cpp -o function_object -Wall -Wextra -Wpedantic -Werror
$ ./function_object
Logger_template_class.h:17 10
Logger_template_class.h:17 50.9
Logger_template_class.h:17 John Grisham
Logger_template_class.h:17 Hello World
Summary
In this chapter, we have learned the importance of class templates. Class templates help in designing generic data stores. We can change the data type of the store with minimal change in the code. The class template also provides generic member functions which can operate on the generic data to provide the same algorithmic solution.
We have learned the syntax of a class template. We have also learned how a class template is declared with examples. Then we have learned how to define the various member functions of a class template. We have defined the no parameter and single parameter constructor of a sample class template.
Then we have briefly understood the need for a copy constructor is in a class. We have defined a copy constructor for the sample storage class. We have finally learned about the copy assignment operator, why they are essential, and how to define the copy assignment operator for the sample storage class. In this chapter, we have very briefly seen how to instantiate a template class.
In the next chapter, we will learn more details about template class instantiation.
Questions
- The STL container vector is an example of a class template in C++. [True/False]
- Class templates are user-defined generic data storage, whereas std::vector is a class template that acts as a container provided by the STL library. [True/False]
- We cannot create an instance of a user-defined class template; instantiation is only possible for STL-defined class templates. [True/False]
- It is not possible to write generic member functions of a class template that can act as the generic data members. [True/False]
- In the case of a copy constructor, the class’s object is created first, and then at a later time, the object is assigned the content of another existing object. [True/False]
- Copy constructors are used for inline initialization of an object at the time of creation. [True/False]
- Copy constructors return the reference to the same object. [True/False]
- The copy assignment operator is another name of copy constructor. [True/False]
- Defining a custom copy constructor is unnecessary because there is a default copy constructor already provided by the compiler. [True/False]
- Default copy constructor performs shallow copy, whereas the custom copy constructors are defined to provide the deep copy. [True/False]
Answers
- True
- True
- False
- False
- False
- True
- False
- False
- False
- True
Leave a Reply