November 21, 2024

How to Write C++ Class Templates – Tutorial on C++ Class Templates for Robotics, Machine Learning, Signal Processing, and Control

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

COPYRIGHT NOTICE: THE TEXT, PHOTOS, AND CODES POSTED ON THIS WEBSITE AND ON THIS WEBPAGE ARE THE OWNERSHIP, INTELLECTUAL PROPERTY, AND COPYRIGHTED BY THE AUTHOR: ALEKSANDAR HABER. THE TEXT AND THE CONTENT OF THIS PAGE SHOULD NOT BE PHYSICALLY COPIED, SHOULD NOT BE COPIED ON OTHER WEBSITES, SHOULD NOT BE REPRINTED, SHOULD NOT BE REPOSTED ON OTHER WEBSITES, SHOULD NOT BE USED AS A LECTURE MATERIAL IN UNIVERSITY COURSES, SHOULD NOT BE USED AS LECTURE MATERIAL IN COURSES ORGANIZED BY AND HOSTED ON ONLINE LEARNING PLATFORMS (such as Udemy, Coursera, etc), AND SHOULD NOT BE USED IN COMMERCIAL SETTING. THE TEXT, PHOTOS, AND CODE PRESENTED ON THIS WEBSITE AND WEBPAGE SHOULD NOT BE INCLUDED IN REPORTS, STUDENT PAPERS, SCIENTIFIC PAPERS, OR IN ANY OTHER PRINTED OR A DIGITAL FORM OR A DOCUMENT WITHOUT WRITTEN CONSENT OF THE AUTHOR. A MONEY FEE MIGHT BE REQUIRED TO REPRINT THE MATERIAL POSTED ON THIS WEBSITE: CONTACT THE AUTHOR: ml.mecheng@gmail.com

CODE COPYRIGHT NOTICE AND LICENSE: THE CODE FILES POSTED ON THIS WEBSITE ARE NOT FREE SOFTWARE AND CODE. IF YOU WANT TO USE THIS CODE IN THE COMMERCIAL SETTING OR ACADEMIC SETTING, THAT IS, IF YOU WORK FOR A COMPANY OR IF YOU ARE AN INDEPENDENT CONSULTANT AND IF YOU WANT TO USE THIS CODE OR IF YOU ARE ACADEMIC RESEARCHER OR STUDENT, THEN WITHOUT MY PERMISSION AND WITHOUT PAYING THE PROPER FEE, YOU ARE NOT ALLOWED TO USE THIS CODE. YOU CAN CONTACT ME AT
ml.mecheng@gmail.com
TO INFORM YOURSELF ABOUT THE LICENSE OPTIONS AND FEES FOR USING THIS CODE. ALSO, IT IS NOT ALLOWED TO (1) MODIFY THIS CODE IN ANY WAY WITHOUT MY PERMISSION. (2) INTEGRATE THIS CODE IN OTHER PROJECTS WITHOUT MY PERMISSION. (3) POST THIS CODE ON ANY PRIVATE OR PUBLIC WEBSITES OR CODE REPOSITORIES. DELIBERATE OR INDELIBERATE VIOLATIONS OF THIS LICENSE WILL INDUCE LEGAL ACTIONS AND LAWSUITS.

NOT TO BE USED FOR AI COPYRIGHT NOTICE: The code and text, as well as all other material on this webpage and the YouTube page, should NOT be used to train an AI algorithm or a large language model, or any type of AI or machine learning algorithm used to recognize, interpret, and draw conclusions from text. Also, it is forbidden to crawl this webpage and to extract information for training an AI algorithm of any sort on the basis of the material presented on this webpage.

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.