Most modern C++ libraries and programs used in robotics, control, and machine learning are based on function and class templates. For example, Robot Operating System (ROS) and Eigen C++ linear algebra libraries heavily depend on and use C++ template functions and libraries. Another example is the Standard Template Library (STL) which represents the core of the modern C++ language. Consequently, if you are a robotics or control engineering student, you should know how to properly define and implement C++ function templates and class templates. The tutorial given below will teach you the basics of the C++ class templates and how to write a class template. The YouTube tutorial accompanying this webpage is given below.
Copyright Notices and NOT to Be Used for AI Notice
Motivation for Defining and Using Class Templates in C++
– The main idea for using class templates in C++ is to make class definitions more generic. That is, we want to define classes whose member functions and variables can deal with various data types and even other objects. While defining the class, we do not want to assume beforehand specific data types for member functions and member variables. We keep them as generic, and the user can choose particular data types when creating an object of a template class. In this way, we can significantly improve the code reusability, and we can write code libraries that can be used in a large number of projects.
For example, in the Standard Template Library (STL), we have a template class called “vector”. Vector template class is used to store dynamic arrays of various types. For example, we can write:
vector <int> v1;
vector <float> f1;
vector <int*> s1;
These statements will declare vectors that can store, integers, floats, and pointers to integers. In this tutorial, through a practical example, we will explain how to define and use C++ template classes.
We define a class called “TwoNumbers”. This class will store two numbers that can be of any type (defined by the user in the driver code). We will create methods that can set, get, and add the two numbers. The class will consist of two constructors. The first default constructor will set both numbers to zero while creating an object of the class. The second overloaded constructor will see the number values while creating an object of the class. Then, we will define a member function that will set the numbers after an object of the class is created, and we will define a function that will retrieve that stored number. Finally, we will define a member function that will sum the two numbers. Although it looks simple, the basic ideas demonstrated in defining this template class can be modified and used in more complex problems.
When defining and implementing C++ function templates it is very important to keep in mind that both the definition and the implementation of class templates should be made in the same (header) file. There are several work-around solutions that can still enable us to separate the definition and implementation, however, complete beginners should stick to the above mentioned rule.
Template Class Definition and Implementation in C++
The header file called “TwoNumbers.h”, and that defines and implements the class called “TwoNumbers” is given below
#ifndef TWONUMBERS_H
#define TWONUMBERS_H
#include<iostream>
#include<cstdlib>
using namespace std;
template<class T>
class TwoNumbers
{
public:
// default constructor
TwoNumbers();
// overloaded constructor
TwoNumbers(T n1, T n2);
// set values of the numbers once the object is created
void setNumbers(T n1, T n2);
// function to return the values of stored numbers
T getNumbers(int pNumber);
// function that will add the stored numbers
T addNumbers();
private:
T firstNumber;
T secondNumber;
};
template <class T>
TwoNumbers<T>::TwoNumbers()
{
firstNumber=0;
secondNumber=0;
}
template <class T>
TwoNumbers<T>::TwoNumbers(T n1, T n2)
{
firstNumber=n1;
secondNumber=n2;
}
template <class T>
void TwoNumbers<T>::setNumbers(T n1, T n2)
{
firstNumber=n1;
secondNumber=n2;
}
template <class T>
T TwoNumbers<T>::getNumbers(int pNumber)
{
if (pNumber==1)
{
return firstNumber;
}
if (pNumber==2)
{
return secondNumber;
}
if ( (pNumber != 1) && (pNumber != 2) )
{
cout<<"Error, the provided index is not 1 or 2 "<<endl;
return 0;
}
}
template <class T>
T TwoNumbers<T>::addNumbers()
{
return firstNumber+secondNumber;
}
#endif
The template class is defined like this:
template<class T>
class TwoNumbers
{
public:
// default constructor
TwoNumbers();
// overloaded constructor
TwoNumbers(T n1, T n2);
// set values of the numbers once the object is created
void setNumbers(T n1, T n2);
// function to return the values of stored numbers
T getNumbers(int pNumber);
// function that will add the stored numbers
T addNumbers();
private:
T firstNumber;
T secondNumber;
};
We start the definition with
template<class T>
class TwoNumbers
{ //body of the class
}
Here, “template<class T>” means that below this statement, a template class is defined where $T$ is a generic data type that is used in the class. We used the letter “T”, however, you can use any other letter for this generic data type. This generic data type is used to define private member variables that store the numbers. “T” can be float, integer, char, or any other data type, and is defined at the moment the class object is created in the driver code that uses this class. In this way, while defining the class, we do not assume any particular form of data type that is stored. That is, private member variables called “firstNumber” and $secondNumber$ can be almost anything and they are later on chosen by the user. This is the power of the template classes.
The private member variables
private:
T firstNumber;
T secondNumber;
are of the T type. Then, we declare the constructor functions and functions for setting and getting the stored numbers. Finally, we declare a function for summing the stored number.
The implementation of the defined classes is
template <class T>
TwoNumbers<T>::TwoNumbers()
{
firstNumber=0;
secondNumber=0;
}
template <class T>
TwoNumbers<T>::TwoNumbers(T n1, T n2)
{
firstNumber=n1;
secondNumber=n2;
}
template <class T>
void TwoNumbers<T>::setNumbers(T n1, T n2)
{
firstNumber=n1;
secondNumber=n2;
}
template <class T>
T TwoNumbers<T>::getNumbers(int pNumber)
{
if (pNumber==1)
{
return firstNumber;
}
if (pNumber==2)
{
return secondNumber;
}
if ( (pNumber != 1) && (pNumber != 2) )
{
cout<<"Error, the provided index is not 1 or 2 "<<endl;
return 0;
}
}
template <class T>
T TwoNumbers<T>::addNumbers()
{
return firstNumber+secondNumber;
}
For example, to define a member function for returning the sum, we write:
template <class T>
T TwoNumbers<T>::addNumbers()
{
return firstNumber+secondNumber;
}
The first line “template <class T>” means that the function is actually a template function of the template class. Then, we need to write “T TwoNumbers<T>::addNumbers()”. Here “T” is the return type, “TwoNumbers<T>” is the name of the class which is now a template, and finally “addNumbers()” is the name of the function.
The driver code for using this class is given below. Make sure that you place this driver code in the same folder in which the header file is saved.
#include<iostream>
#include<cstdlib>
#include "TwoNumbers.h"
int main()
{
TwoNumbers<int> PairNumber(-5,7);
cout<<PairNumber.getNumbers(1)<<endl;
cout<<PairNumber.getNumbers(2)<<endl;
cout<<PairNumber.getNumbers(5)<<endl;
cout<<"The sum of two numbers is "<< PairNumber.addNumbers()<<endl;
TwoNumbers<float> PairNumberFloat(3.2,-1.4);
cout<<PairNumberFloat.getNumbers(1)<<endl;
cout<<PairNumberFloat.getNumbers(2)<<endl;
cout<<"The sum of two numbers is "<< PairNumberFloat.addNumbers()<<endl;
return 0;
}
We create objects of the template class by using this syntax:
TwoNumbers<int> PairNumber(-5,7);
TwoNumbers<float> PairNumberFloat(3.2,-1.4);
Here <int> and <float> mean that we are creating a particular class objects that will set T=int and T=float. This is how we create template class objects. We have to tell to compiler the data type that the class will store, that is, by writing TwoNumbers<data_type>, we are saying to the compiler T=data_type.