Function Templates in C++

What are function templates in C++? Why and how are they used ?

Solution:

Let us write a simple function to compute sum of two integers

    int sum(int a, int b)
    {
        return a+b;
    }

If we want to find the sum of two floats, then above function will not give the desired result. So we have to write another function (C++ allows function overloading, so we can keep the name same)

    float sum(float a, float b)
    {
        return a+b;
    }

Similarly, if we want to compute the sum of two double values, the function will change to

    double sum(double a, double b)
    {
        return a+b;
    }

So we have three functions with same functionality (body of function) and just differ on the parameter type.

There are two basic problems with this approach (overloading functions which have exactly same functionality and just differ on types).

  • Maintainability: Suppose we want to add functionality that when addition of two numbers is performed then log that in a text file. We have to put that logging functionality in all the three places.
  • Addition: If we want to compute sum of two long values (or any other user type) then we have to write separate functions. There will be lot of duplication of code this way.
Next: C Language Solution ... C Language Solution (Macros):

C language tried to solve this problem by MACROS, which are type independent, but Macros comes with side effects.

For example: The below macro will work fine for some, but may give unexpected results in some other cases

    #define SQUARE(X) (X)*(X)
    ... ...
    int a = SQUARE(2+3);   // OK.. will compute (2+3)*(2+3)
    int b = SQUARE(a++);   // UNDEFINED.. will compute (a++) * (a++)
Next: C++ Solution ... C++ Solution (templates):

C++ introduced function templates and class templates. Since we are only looking at function templates in this question.

Function Templates are functions that can operate with generic types. i.e the functionality of Function templates can be adapted to more than one type (class is also a type) without repeating the entire code for each type.

The above function to compute sum will be written as

    template  T sum(T a, T b)
    {
        return a + b;
    }

If we call sum for int values, then compiler will generate the below function definition for us

    int sum(int a, int b)
    {
        return a+b;
    }

If we call sum for float values, then compiler will generate the below function definition for us

    float sum(float a, float b)
    {
        return a+b;
    }

i.e Compiler will use the function Template to generate the actual function definition during compilation. From developer point of view, we have to write just one function template.

In case of conflict in the type of parameters, you may use the below(explicit) syntax to call template function

    int p;
    double q;
    sum<double>(p, q);   //Compiler can't determine the mixed type
Next: Declaring Function Templates ... Declaring template functions (Prototype):

While declaring the function also you have to specify that it is a template and not a usual function. The above function will be declared as below:

    template<typename T> T sum(T, T);
Next: Overloading template functions ... Overloading template functions:

Template functions can be overloaded with usual functions or other template function. In case they are overloaded with usual function, the non-template function will only be called in case of exact match. For example:

    template<class T> void fun(T x, T y)
    {
        cout << "Template" << endl;
    }
    void fun(int w, int z)
    {
        cout << "Non-Template" << endl;
    }
    int main()
    {
        fun( 1 ,  2 );          // Non-Template. Exact Match
        fun('a', 'b');          // Template.
        fun( 1 , 'b');          // Non-Template. Erroneous to call template
        fun<char>('a', 'b');    // Template. Explicit Template call
    }
Next: Example ... Example:

Lets write the template function that can sort array, of any data type, using quick sort:

    template <typename T> void quickSort(T *arr, int low, int high)
    {
        if(low < high)
        {
            int mid = partition<T>(arr, low, high);
            quickSort<T>(arr, low, mid-1);
            quickSort<T>(arr, mid+1, high);
        }
    }
    template<typename T> int partition(T *arr, int low, int high)
    {
        int pivot = l;   // Index of Pivot element
        while(low < high)
        {
            while(arr[low] <= arr[pivot])
                l++;      // Move l forward
            while(arr[high] > arr[pivot])
                h--;       // Move h backward
            if(low < high)
                swap(arr[low], arr[high]);
        }
        /* h is final position for the pivot */
        swap(arr[pivot], arr[high]);
        return high;
    }

0 Comments

Leave a comment