Harold Serrano

View Original

Understanding Function Templates in C++

Let me start off by giving you a tip:

Avoid code duplication as if it was the plague.

I mean this. Code duplication only brings trouble and duplication of effort. For example, assume that you want to find the largest of two int values. The code below shows how you would get the largest of two ints.

int const& max(int const& a, int const& b){
    return a<b?b:a;
}

But, what if you also need to get the largest value between two floats? Then, you would do something like this:

float const& max(float const& a, float const& b){
    return a<b?b:a;
}

Having many functions doing the same computation is not a good programming practice. In cases like this, you are better off using Function templates.

What are Function Templates?

Function templates provide functional behavior for different types. For example, the template below can find the largest value of ints, floats, etc:

//declaring a template in maximum.h
template <typename T> //template parameter
inline T const& maximum(T const&a, T const&b){
    //if a<b then use b else use a
    return a<b?b:a;  
}

In the main() function I can call the function template to find the largest of different types:

int main(int argc, const char * argv[]) {

    std::cout<<maximum(3,5)<<std::endl;
    std::cout<<maximum(4.5,2.0)<<std::endl;            
    std::cout<<maximum(std::string("Harold"),std::string("Serrano"))<<std::endl;

    return 0;
}

Result:

maximum of ints: 5
maximum of floats: 4.5
maximum of strings: Serrano

By using function templates, you avoid any code duplication in your program.

So how does the C++ compiler handle templates and how does it know which type to call?

The compiler determines the template parameter T by the arguments you pass. If you passed two ints to the parameter type T, the C++ compiler concludes that T must be int.

During compilation, the C++ compiler instantiate each function template call.

An Instantiation means that the compiler will replace the template parameters by concrete types. For example, during the first call to maximum() the arguments passed were ints:

int main(int argc, const char * argv[]) {
    //arguments passed to function template are ints
    std::cout<<maximum(3,5)<<std::endl;
    //...

    return 0;
}

Thus, the compiler will create an instance of the template with concrete int types as:

inline int const& maximum(int const&a, int const&b){
    //if a<b then use b else use a
    return a<b?b:a;  
}

This means that templates are actually compiled twice:

  1. Without instantiation, the compiler checks the template code for correct syntax. Such as missing semicolons, etc.
  2. At the time of instantiation, the template code is checked to ensure that all calls are valid. Such as unsupported function calls.

Templates are kind of cool to use. Once you set it up, you can use it for different types. Try to use them whenever you find yourself duplicating code.

PS. Sign up to my newsletter and get development tips