Templates in C++ provides a mechanism that allows the creation of generic data types for classes and functions, templates can be instantiated by passing specific data types to them, which gives the advantage of using the same classes and functions with different data types without rewriting them.

We have mainly two kinds of templates:

  • Function Templates
  • Class Templates

C++ Templates

Templates are expanded at compile-time like macros, but templates are unlike macros they provide type checking and all the features of functions and classes let’s see each type of template in detail.

Function Template

A function template looks similar to normal function declarations except it has a generic definition of the template above the function; which can be used as a generic data type inside the function, let’s see the format of how the function templates can be declared.

template <class identifier> function_declaration;
template <typename identifier> function_declaration;

Both definitions work in a similar manner, template class/typename is keywords provided by the compiler, whereas the identifier is the user-defined name, and function declaration is the actual function implementation for e.g. we have defined a template function for addition.

template <typename T>
T add (T n1, T n2){
            return (n1+n2);
}

This is how a function template can be declared; the template can be called by following way:

int main(){
            cout<< add<int,int>(10,20)<<endl;
            cout<<add<float,float>(25.5,30.5)<<endl;
            return 0;
}

Output:

30
60

Here the compiler will internally create two different functions one for the addition of integers and another for the addition of floats as seen below

int add(int n1, int n2){
return (n1 +n2);
}

float add(float n1, float n2){
            return (n1 +n2);
}

The add (10, 20) will call the int add (int, int) function whereas add (25.5, 30.5) will call float add (float, float) function, function templates are generally used in the situations where we want to perform the same operations on different data types, a typical example can be sorting of numbers, we can write a sorting algorithm and then pass different data types for sorting. Here is another example for function templates.

#include <iostream>
using namespace std;
template <typename T>
void bubbleSort(T a[], int n) {
            for (int i = 0; i < n - 1; i++)
                        for (int j = n - 1; i < j; j--)
                                    if (a[j] < a[j - 1]) {
                                                swap(a[j], a[j - 1]);
}
}
int main() {
            int a[5] = {20, 70, 30, 10, 40};
            bubbleSort<int>(a, 5);
            cout << "Integer sorted array : ";
            for (int i = 0; i < 5; i++)
                        cout << a[i] << " ";
            cout << endl;
            float b[5] = {20.1, 70.7, 30.2, 10.5, 40.3};
            bubbleSort<float>(b, 5);
            cout << " Float sorted array : ";
            for (int i = 0; i < 5; i++)
                        cout << a[i] << " ";
            cout << endl;

return 0;
}

Output:

Integer sorted array
10   20   30   40   70

Float sorted array
10.5   20.1   30.2   40.3   70.7

We can see in above example same function template which implements bubble sorting algorithm can be used for sorting integers and floats both, we have demonstrated example for integer and float but we can use many data types in above scenario like long, double, char, unsigned char etc.

Class Template

Class template has similar syntax as function templates, in class templates also we define template syntax above class with generic data type which can be used inside the class, and an example can help us to understand.

#include <iostream>
using namespace std;
template <typename T>
class List {
private:
T *ptr;
int size;
public:
List(T arr[], int s);
void display();
};

template <typename T>
List<T>::List(T arr[], int s) {
ptr = new T[s];
size = s;
for(int i = 0; i < size; i++)
ptr[i] = arr[i];
}

template <typename T>
void List<T>::display() {
for (int i = 0; i < size; i++)
cout<<" "<<*(ptr + i);
cout<<endl;
}

int main() {
int arr[5] = {2, 4, 8, 10, 12};
List<int> lst(arr, 5);
lst.display();
return 0;
}

Output:

2   4   8   10   12

 

Arguments of Templates

We can have more than one argument to the template functions and classes; we can also have default arguments to the templates. The following example demonstrates how we can have more than one argument for function and class templates.

#include <iostream>
using namespace std;
template <typename T1, typename T2>
class X {
private:
T1 a;
T2 b;
public:
X( ) { cout<<”X constructor called”<<endl; }
};

int main(){
X <int, float>x1,
X <char, double>x2;
return 0;
}

Output:

X constructor called
X constructor called

Like we provide default argument to functions in a similar way we can pass default argument to templates as well, following example demonstrates default arguments to function templates and class templates.

#include<iostream>
using namespace std;
template <typename T, typename D = char>
class X {
public:
T a;
D b;
X() { cout<<"Constructor Called"<<endl; }
};

int main() {
X<int> x;            // this will call x<int, char>
return 0;
}

Output:

Constructor Called

Earlier we have seen function overloading in polymorphism where the same function name can be called with different parameters, but the implementation was different for each function, but in templates, we have the same implementation and the compiler internally creates different functions which are called based on the data types passed during the creation.

Static Variables and Function Templates

For every function template, a local copy of the static variable is created and its scope remains till the lifetime of the function or class. Let’s understand this through examples.

#include <iostream>
using namespace std;
template <typename T>
void func1(T t1)
{
static int x = 50;
cout << ++x;
return;
}

int main()
{
func1<int>(10);
cout << endl;
func1<int>(20);
cout << endl;
func1<double>(1.1);
cout << endl;
getchar();
return 0;
}

Output:

51
52
51

We can see in the above example for integer data type one copy of function is created and so one local copy of the static variable is also created on incrementing the static variable it increments from 50 to 51 and to 52, whereas for double data type new function is created and so new local static variable is created which results in the value of the static variable as 51.

Static Variables and Class Templates

Static variables for class templates behave similarly to function templates; class templates maintain a local copy for each static variable, the scope of the static variable remains till the scope of the object.

#include <iostream>
using namespace std;
template <typename T> class A
{
T val;
public:
static int cnt;
A() {
cnt++;
}
};

template<typename T>
int A<T>::cnt = 0;
int main()
{
A<int> a;
A<int> b;
A<double> c;
cout << A<int>::cnt << endl;
cout << A<double>::cnt << endl;
getchar();
return 0;
}

Output:

2
1

As we can see value for the class template with an integer is incremented twice since we have created two objects of class A but only one copy of the static variable is maintained, and for double it’s incremented once as we have created one copy of the class with the double data type.

Function Template Specialization

We have seen that templates can be created and used for the data types passed, but in real-time we may face situations where all data types may not apply for the operation performed inside the function for e.g. consider a function template for the addition of numbers, but this cannot be applied for addition of two string data type, so we can have specific implementation for such scenario which is called as template specialization.

Let’s see an example:

template <typename T>
T add (T n1, T n2){
return (n1+n2);
}
template <>
string add (string s1, string s2){
string s3= strcat(s1,s2);
return (s3);
}

As we can see we have implemented a special behaviour for string data type, so whenever a template with string data type is created the second version will automatically get called, some other examples where we need special implementation might be like sorting, lists, sets where functions for some data types need special implementation.

Class Template Specialization

Similar to function template specialization we can also have class template specialization, we can implement special behaviour for required data types. Let’s see an example.

#include <iostream>
using namespace std;
template <typename T>
class A
{
public:
A() {
}
};

template <>
class A <int>
{
public:
A() {
}
};

int main() {
A<int> a;
A<char> b;
A<float> c;
return 0;
}

We can see that we have implemented special behaviour for the class template for integer data type, so whenever the template is created using integer data type the second version of class A <int> is called.

Passing Non-Type Arguments to Templates

Passing non-type arguments mainly help in passing min and max values to the templates, these values should be always constants and should know at the compile time, since templates are evaluated at compile-time, so the below example shows the passing of non-type arguments to templates.

#include <iostream>
using namespace std;
template <typename T, int max>
void array(T a[], int n){
//code for array operations
}

int main() {
int arr[] ={1,2,3,4};
array <int, 500> (arr,4);
}

As we can see in the above example we have passed 500 as a constant value which can be used in the template function and it’s a constant if we replace the value of 500 with any variable we will get a compilation error.

Variadic Template

So far we have seen templates with a fixed number of arguments, which had to be, specified when the template was created, but C++ 11 provides a facility to pass a variable number of arguments to the function templates.

template <typename… Values> class collection;

The above template class collection will take any number of arguments passed to the template when its created, here we have created the template with four arguments

collection <char *, int, float, string> object_name;

We can pass zero arguments as well, so the collection <> object_name; will be created without any parameters. Variadic templates can also be applied to functions, which can provide type-safe facilities to variadic functions kind of print which needs variable kind of arguments.

template<typename... params> void printf(const std::string &str_format, params... parameters);