C++ Templates – An Introduction

Why we need Templates?

Suppose we are writing a function to compare the values of two variables and return the one with greater value, the function would look like this (for an int):

1
2
3
4
int max(int a, int b)
{
    return a > b ? a : b;
}

what if we would like to support double, long int, char, unsigned int?

More code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
double max( double a, double b)
{
    return a > b ? a : b;
}

float max( float a, float b)
{
    return a > b ? a : b;
}

char max( char a, char b)
{
    return a > b ? a : b;
}

unsigned int max( unsigned int a, unsigned int b)
{
    return a > b ? a : b;
}

long max( long a, long b)
{
    return a > b ? a : b;
}

hmmm, works like a charm. But you have to write definitions (overload) if you want more types to be supported. It gets ugly very quickly with moderately complex logic. So wouldn’t it be nice to have some language feature that could do the work of replication of code for us? Well it happens that C++ supports such feature, infamously known as Templates.

Templates 101

Consider these three definitions of max

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
int max(int a, int b)
{
    return a > b ? a : b;
}

double max( double a, double b)
{
    return a > b ? a : b;
}

float max( float a, float b)
{
    return a > b ? a : b;
}

if you observe closely you will notice that all three definitions look similar. So if we try to abstract the structure such that the logic remains same but the types vary then we should be able to write something like this:

1
2
3
4
5
template<class T>
const T& max( const T& a, const T& b)
{
    return a > b ? a : b;
}

Lets dissect the above code:

1
template<class T>

This statement defines the Template Function, in our case it is function but it can be a class as well, that is being parametrized with a single type T. T will be replaced by required type, we will get into this a bit later.

1
2
3
4
const T& max( const T& a, const T& b)
{
    return a > b ? a : b;
}

Here the function max takes in two arguments a and b and returns the one that is greater of the two. The type of these arguments is T, as in template<class T> above.

Ok so we are done with the description and here is the how the compiler generates the code for various types on the fly.

1
2
3
4
5
6
7
int main()
{
    int first = 9;
    int second = 10;
    int larger = max(first, second);
    return 0;
}

Here compiler see that function max is being called with two integers, note both the arguments have to be of same type with templates as no implicit type conversion happens like int –> double, so compiler can successfully deduce that we want the max function definition for an int. Compiler creates a new copy of the template code and replaces T with int.

This act of creating functions (and classes as well) on the fly from Template Definitions is called Template Instantiation. Template Instantiation is two step process:

  1. Compiler checks Template Definition for syntatic inconsistencies, in our case the Templated Parameter T is being compared (operator > ), here compiler assumes that the type T, can be user defined type as well, defines operator >
  2. Once the compiler finds that the Template Definition is syntactically consistent, then in second step it checks for sematic inconsistencies, operator > is a good example here. If we have our own user defined class Comparable, then for it to work with Template Function max, we will have to define operator > for class Comparable. When instantiating Templates compiler creates a copy of the code, hence it needs to see the entire definition. Thus the Templates are usually placed in header files.

Here is the Comparable class:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Comparable
{
private:
    int number;
public:
    Comparable(int num) : number(num)
    {}

    bool operator&gt;(const Comparable& other) const
    {
        return this-&gt;number &gt; other.number;
    }
};

    
// And here the Comparable class is being used with Template Function max
    
int main()
{
    Comparable first(9);
    Comparable second(10);
    const Comparable& larger = max(first, second);
    return 0;
}

Thus you can now use Template Function max with any user defined type, if that type provides operator > for comparison. We have barely scratched the surface about what can be done with C++ Templates, nonetheless it is good beginning. In coming posts I will cover Template classes and various scenarios. I will briefly cover STL (Standard Template Library) and BOOST library.

 
C++