Templates in C++

Introduction

Templates are a cornerstone feature of C++, providing a powerful mechanism for writing generic code. They allows developers to create functions and classes that operate on types rather than specific values, promoting code reuse and flexibility.

Template

A template is a feature that allows you to write generic code that can work with different data types. Templates provide a way to define functions and classes without specifying the actual data type they will operate on. Instead, you use placeholders called template parameters to represent type or values that will be provided when the template is instantiated.

template <typename T>
T add(T a, T b) {
    return a + b;
}

In this example, typename T is a template parameter representing a data type. The add function can work with any type (int, float, double, etc.) because the actual type is determined when the function is called.

Usage of the add function

int result1 = add(5, 3);        // instantiates add<int>(int, int)
double result2 = add(3.5, 2.7); // instantiates add<double>(double, double)

How do Templates Work?

Templates are expanded at compile time. This is like macros. The difference is, that the compiler does the type-checking before template expansion. The idea is simple, source code contains only function/class, but compiled code may contain multiple copies of the same function/class.

Types of Templates in C++

There are two types of templates in C++

  • Function template
  • Class template

Function Templates:

Function templates allow you to write a generic function that can work with various data types. The syntax involves declaring a template parameter within angle brackets (<>), which is then used as the type of one or more function parameters or the return type.

 template <typename T>
T add(T a, T b) {
    return a + b;
}

The typename keyword is interchangeable with class in template declarations.

  • T is the type of argument or placeholder that can accept various data types.
  • typename || class is a key word to specify a generic type in a template declaration. 
#include <iostream>
using namespace std;
 
// One function works for all data types.  This would work
// even for user defined types if operator '>' is overloaded
template <typename T>
T myMax(T x, T y)
{
    return (x > y) ? x : y;
}
 
int main()
{
    // Call myMax for int
    cout << myMax<int>(3, 7) << endl;
    // call myMax for double
    cout << myMax<double>(3.0, 7.0) << endl;
    // call myMax for char
    cout << myMax<char>('g', 'e') << endl;
 
    return 0;
}

Output

7
7
8

C++ Function Template Syntax

template <typename type>
ret-type func-name(parameter list)
{
	// body of the function
}

or 

template <class type>
ret-type func-name(parameter list)
{
	// body of the function
}

Here, type is a placeholder name for a data type used by the function. we can use either of class or typename word.

Overloading of Template Function in C++

Overloading is the feature that allows the specification of more than one function of the same name in the same scope.

So, by overloading template functions in C++, we can define function templates in C++ having the same but called with different arguments.

For example:

#include <iostream>
using namespace std;

// Declaring the template function.
template <class T>

// Overloading the template function.
void display(T t1)
{
   cout << "Inside the display template function: " << t1 << endl;
}

// Overloading the template function with an integer parameter.
void display(int t1)
{
   cout << "Inside the overloaded integer-display template function: " << t1 << endl;
}

// main function
int main()
{
   // Calling the overloaded template function with different arguments.
   display(20);
   display(20.55);
   display('G');

   return 0;
}

Output

Inside the overloaded integer-display template function: 20
Inside the display template function: 20.55
Inside the display template function: G

Class Template Declaration

A class template starts with the keyword template followed by template parameter(s) inside <> which is followed by the class declaration.

template <class T>
class className {
  private:
    T var;
    ... .. ...
  public:
    T functionName(T arg);
    ... .. ...
};

In the above declaration, T is the template argument which is a placeholder for the data type used, and class is a keyword.

Inside the class body, a member variable var and a member function functionName() are both of type T.

Creating a Class Template Object

Once we have declared and defined a class template, we can create its objects in other classes or functions (such as the main() function) with the following syntax:

className<dataType> classObject;

For example,

className<int> classObject;
className<float> classObject;
className<string> classObject;

Complete Example:

// C++ program to demonstrate the use of class templates

#include <iostream>
using namespace std;

// Class template
template <class T>
class Number {
   private:
    // Variable of type T
    T num;

   public:
    Number(T n) : num(n) {}   // constructor

    T getNum() {
        return num;
    }
};

int main() {

    // create object with int type
    Number<int> numberInt(7);

    // create object with double type
    Number<double> numberDouble(7.7);

    cout << "int Number = " << numberInt.getNum() << endl;
    cout << "double Number = " << numberDouble.getNum() << endl;

    return 0;
}

Output:

int Number = 7
double Number = 7.7

Class Templates With Multiple Parameters

In C++, we can use multiple template parameters and even use default arguments for those parameters. For example,

template <class T, class U, class V = int>
class ClassName {
  private:
    T member1;
    U member2;
    V member3;
    ... .. ...
  public:
    ... .. ...
};
#include <iostream>
using namespace std;

// Class template with multiple and default parameters
template <class T, class U, class V = char>
class ClassTemplate {
   private:
    T var1;
    U var2;
    V var3;

   public:
    ClassTemplate(T v1, U v2, V v3) : var1(v1), var2(v2), var3(v3) {}  // constructor

    void printVar() {
        cout << "var1 = " << var1 << endl;
        cout << "var2 = " << var2 << endl;
        cout << "var3 = " << var3 << endl;
    }
};

int main() {
    // create object with int, double and char types
    ClassTemplate<int, double> obj1(7, 7.7, 'c');
    cout << "obj1 values: " << endl;
    obj1.printVar();

    // create object with int, double and bool types
    ClassTemplate<double, char, bool> obj2(8.8, 'a', false);
    cout << "\nobj2 values: " << endl;
    obj2.printVar();

    return 0;
}

Output

obj1 values: 
var1 = 7
var2 = 7.7
var3 = c

obj2 values: 
var1 = 8.8
var2 = a
var3 = 0

In the above program, we have created a class template, named ClassTemplate, with three parameters, with one of them being a default parameter.

Notice the code class V = char. This means that V is a default parameter whose default type is char.

Inside ClassTemplate, we declared 3 variables var1, var2 and var3, each corresponding to one of the template parameters.

In main(), we create two objects of ClassTemplate with the code.