Functions III (Templates)

Learn the functionality of templates in C++.

Swap multiple data types

When we encounter a scenario where we need to perform an operation on different data types, it can be tempting to write a separate function for each type. However, this can quickly lead to redundant code and make maintenance of the program code difficult.

For example, we have a swap function that can only swap two integer values. What if we have to swap int, double, and float values in our program? One possible solution is to write a separate swap function for each data type.

If we define a separate swap function for each data type, our program would look like the following code:

#include <iostream>
using namespace std;
void swap_value_int(int &a, int &b)
{
int temp = a;
a = b;
b = temp;
}
void swap_value_float(float &a, float &b)
{
float temp = a;
a = b;
b = temp;
}
int main()
{
int num1 = 5;
int num2 = 4;
cout << "Before Swap" << '\n';
cout << "num1 : " << num1 << '\n';
cout << "num2 : " << num2 << '\n';
swap_value_int(num1, num2);
cout << "After Swap" << '\n';
cout << "num1 : " << num1 << '\n';
cout << "num2 : " << num2 << '\n';
float num3 = 5.2;
float num4 = 4.1;
cout << "Before Swap" << '\n';
cout << "num3 : " << num3 << '\n';
cout << "num4 : " << num4 << '\n';
swap_value_float(num3, num4);
cout << "After Swap" << '\n';
cout << "num3 : " << num3 << '\n';
cout << "num4 : " << num4 << '\n';
return 0;
}
Two separate swap functions (with different names) for int and float values

In SwapWithDifferentNames.cpp:

  • Lines 3–8: We implement the swap_value_int function for swapping the int values.

  • Lines 9–14: We implement the swap_value_float function for swapping the float values.

In SwapWithFunctionOverloading.cpp:

  • The only difference in this code is that we have named the two functions with exactly the same name, SWAP(), but the arguments are different. This is called function overloadingTwo or more functions with the same name but different parameters..

In the example above, we have implemented the swapping functionality for both data types int and float. What if we have two more swapping data types that need to be implemented for this purpose? This is not efficient because, for each data type, we will need a separate function.

Can we avoid this?

Template functions

In C++, a template function can operate on different types of data without the need for separate implementations for each type. It allows us to write a single function that can work with different data types, such as integers, floats, or even custom data types, without having to create separate functions for each type.

Template functions are defined using a special syntax that includes the template keyword followed by a list of template parameters, which can be either type names or non-type parameters like integers or pointers. These parameters are used to define a generic function that can be instantiated with different data types at compile-time.

For example, consider the following template function provided by C++ that calculates the maximum value between two values:

template <typename T>
T max(T a, T b)
{
return (a > b) ? a : b;
}
A generic max function

Here, the typename T is a template parameter that represents a type. This function can be used to find the maximum value between two integers, floats, or any other data type that supports the > operator.

Let’s test the max function above.

Press + to interact
#include <iostream>
using namespace std;
int main()
{
int a = 10, b = 20;
float c = 3.14, d = 2.71;
// Use the max function with different types
cout << max(a, b) << endl; // Output: 20
cout << max(c, d) << endl; // Output: 3.14
return 0;
}

Using template functions, we can write more generic and reusable code that works with different data types while avoiding the need to write separate functions for each one.

How does it work?

Let’s look at another implementation of the swap function. This time, we will make it generic enough that it will run for any data type.

Press + to interact
#include <iostream>
using namespace std;
template<class T>
void swap_value(T &a, T &b)
{
T temp = a;
a = b;
b = temp;
}
int main()
{
int num1 = 5;
int num2 = 4;
swap_value(num1, num2);
float num3 = 5.2;
float num4 = 4.1;
swap_value(num3, num4);
return 0;
}

The compiler identifies the desired types for which we want to execute the generic function but doesn’t create copies of that function with all the types at compile-time. Instead, it generates a version of the function at the point where the function is called with the specific type. This process is called instantiation.

When we call the function with a specific type, the compiler generates a version of that function with the given type and compiles it. This way, the compiler generates the function definition only for the specific type(s) it needs at runtime rather than creating copies for all possible types.

Press + to interact
Templates' behavior at compile-time
Templates' behavior at compile-time

Therefore, the behavior of the templates can be described as a mechanism for generating code for different types at compile-time based on the code written using a generic type.

Let’s see how templates work by executing the code with proper messages printed on the console for various data types.

Press + to interact
#include <iostream>
using namespace std;
template<class T>
void swap_value(T &a, T &b)
{
T temp = a;
a = b;
b = temp;
}
int main()
{
int num1 = 5;
int num2 = 4;
cout<<"Before Swap"<<'\n';
cout << "num1 : " <<num1<<'\n';
cout << "num2 : " <<num2<<'\n';
swap_value(num1, num2);
cout<<"\nAfter Swap"<<'\n';
cout << "num1 : " <<num1<<'\n';
cout << "num2 : " <<num2<<'\n';
float num3 = 5.2;
float num4 = 4.1;
cout<<"\nBefore Swap"<<'\n';
cout << "num3 : " <<num3<<'\n';
cout << "num4 : " <<num4<<'\n';
swap_value(num3, num4);
cout<<"\nAfter Swap"<<'\n';
cout << "num3 : " <<num3<<'\n';
cout << "num4 : " <<num4<<'\n';
return 0;
}

Tip: Run the program and see how the swap_value() function works with only one template implementation.

We’ll now solve another nontrivial problem to gain more practice with templates. In particular, we’ll explore how to use a specific function for multiple data types.

Exercise: Generic maximum function

Your task is to create a robust function called MAX(). It’ll have the following properties:

  • It takes five input parameters, including integers, floating points, and characters.

  • It returns the maximum value among the inputs.

The main() function has already been provided for you to make the task simpler.

Note: You are not allowed to overloadWriting a separate function for each data type with the same name. the MAX() funtion.

Press + to interact
#include <iostream>
using namespace std;
int main()
{
int a_i = 3, b_i = 19, c_i = 4, d_i = 14 ,e_i = 12;
float a_f = 12.9, b_f = 23.4, c_f = 9.0, d_f = 2.4, e_f = 14.3;
char a_c = 'g', b_c = 'e', c_c = 'r', d_c = 'b', e_c = 'l';
cout<< MAX(a_i, b_i, c_i, d_i, e_i) << '\n';
cout<< MAX(a_f, b_f, c_f, d_f, e_f) << '\n';
cout<< MAX(a_c, b_c, c_c, d_c, e_c) << '\n';
}