In this C++ tutorial for machine learning, control, and robotics, we provide a concise explanation of C++ function templates. The YouTube video accompanying this tutorial is given below.
Motivation for Using Function Templates in C++
C++ provides us with a capability to define generic functions that accept arguments of almost any type. That is, when defining a function, we define a function using a generic data type, and then when we instantiate or create a particular function, we use a concrete data type. That is, we use templates when we want to define a function or a class that can accept almost any argument.
Let us start with a motivating example. Let us suppose that we want to write a function that will sum two numbers and print the resulting value. Also, let the requirement be that the function should work for any data type. That is, we can send floats or integers or combinations of floats and integers as data types. The function should work in any case. To do that, we can start with the following program
// PROBLEM: DEFINE A FUNCTION THAT WILL PRINT THE SUM OF TWO NUMBERS
// THE NUMBERS CAN BE OF ANY DATA TYPE
#include<iostream>
#include<cstdlib>
using namespace std;
void printSum(int a1,int a2)
{
cout<<"The sum of two numbers is "<<a1+a2<<endl;
}
int main()
{
int t1=7;
int t2=3;
printSum(t1,t2);
return 0;
}
This program should work just fine as long as we send integers to the function. However, let us see what happens when we send floats to the function as input arguments:
// PROBLEM: DEFINE A FUNCTION THAT WILL PRINT THE SUM OF TWO NUMBERS
// THE NUMBERS CAN BE OF ANY DATA TYPE
#include<iostream>
#include<cstdlib>
using namespace std;
void printSum(int a1,int a2)
{
cout<<"The sum of two numbers is "<<a1+a2<<endl;
}
int main()
{
int t1=7;
int t2=3;
float t3=7.3;
float t4=2.7;
//printSum(t1,t2);
printSum(t3,t4);
return 0;
}
The two floats are $t3=7.3$ and $t4=2.7$. The result should be 10. However, the function returns $9$. What is the reason behind this? Well, the function expects to see integers as inputs. If we send floats as inputs, the floats will be converted to integers by simply truncating the decimal number. Conequently, the function converts $7.3$ to $7$, and $2.7$ to $2$, and then it adds $7$ and $2$, which produces $9$. To fix this problem, we use function templates:
// PROBLEM: DEFINE A FUNCTION THAT WILL PRINT THE SUM OF TWO NUMBERS
// THE NUMBERS CAN BE OF ANY DATA TYPE
#include<iostream>
#include<cstdlib>
using namespace std;
void printSum(int a1,int a2)
{
cout<<"The sum of two numbers is "<<a1+a2<<endl;
}
template <typename T, typename U>
void printSumTemplate(T n1, U n2)
{
cout<<"The sum of two numbers is "<<n1+n2<<endl;
}
int main()
{
int t1=7;
int t2=3;
float t3=7.3;
float t4=2.7;
//printSum(t1,t2);
//printSum(t3,t4);
printSumTemplate(t3,t4);
return 0;
}
We define the template functions like this
template <typename T, typename U>
void printSumTemplate(T n1, U n2)
{
cout<<"The sum of two numbers is "<<n1+n2<<endl;
}
We define the template functions by first typing “template <typename T, typename U>”. This means that our template function depends on two generic data types “T” and “U”. Then, instead of using float or int, we can simply use $T$ and $U$ instead. That is, we can write: “void printSumTemplate(T n1, U n2)”. Then, we can simply use $n1$ and $n2$ in our function. In the main file, we can call the template function like this:
printSumTemplate(t3,t4);
Behind the scenes, the compiler will recognize that $t3$ and $t4$ are floats, and it will perform correct processing. The result is $10$. On the other hand, we can also call the function $printSumTemplate$ with the arguments with different data types. We can also call
printSumTemplate(t3,t1);
The result is $14.3$. That is, we can call the function by using input arguments of the mixed type. Here, $t3$ is float and $t1$ is integer. The compiler properly recognizes this, and performs the proper actions since the template function is defined accordingly. However, there is one critical issue when calling template functions in the main function like this:
printSumTemplate(t3,t4);
Here, the issue is that the compiler automatically deduces that the template variable T is float and the template variable U is float. Also, here
printSumTemplate(t3,t1);
the compiler will automatically deduce that the template variable T is float and that the template variable U is integer. This is not what we want. The correct approach is to precisely (explicitly) specify the types when calling the template function. We do it like this:
printSumTemplate<float,float>(t3,t4);
printSumTemplate<float,int>(t3,t4);
The complete code should look like this
// PROBLEM: DEFINE A FUNCTION THAT WILL PRINT THE SUM OF TWO NUMBERS
// THE NUMBERS CAN BE OF ANY DATA TYPE
#include<iostream>
#include<cstdlib>
using namespace std;
void printSum(int a1,int a2)
{
cout<<"The sum of two numbers is "<<a1+a2<<endl;
}
template <typename T, typename U>
void printSumTemplate(T n1, U n2)
{
cout<<"The sum of two numbers is "<<n1+n2<<endl;
}
int main()
{
int t1=7;
int t2=3;
float t3=7.3;
float t4=2.7;
//printSum(t1,t2);
//printSum(t3,t4);
//printSumTemplate(t3,t1);
printSumTemplate<float,int>(t3,t1);
return 0;
}
We can also define template functions as follows. Namely, instead of using the original definition involving “typename”, we can use “class” keyword as follows:
template <class T, class U>
void printSumTemplate2(T n1, U n2)
{
cout<<"The sum of two numbers is "<<n1+n2<<endl;
}
The complete code is given below
// PROBLEM: DEFINE A FUNCTION THAT WILL PRINT THE SUM OF TWO NUMBERS
// THE NUMBERS CAN BE OF ANY DATA TYPE
#include<iostream>
#include<cstdlib>
using namespace std;
void printSum(int a1,int a2)
{
cout<<"The sum of two numbers is "<<a1+a2<<endl;
}
template <typename T, typename U>
void printSumTemplate(T n1, U n2)
{
cout<<"The sum of two numbers is "<<n1+n2<<endl;
}
template <class T, class U>
void printSumTemplate2(T n1, U n2)
{
cout<<"The sum of two numbers is "<<n1+n2<<endl;
}
int main()
{
int t1=7;
int t2=3;
float t3=7.3;
float t4=2.7;
//printSum(t1,t2);
//printSum(t3,t4);
//printSumTemplate(t3,t1);
//printSumTemplate<float,int>(t3,t1);
printSumTemplate2<float,int>(t3,t1);
return 0;
}