Templated Functions

download the slides used in this presentation
PowerPoint pptx | Acrobat pdf

Objectives

While engaging with this module, you will...

  1. learn what a templated function is
  2. apply the declaration and use of templated functions
  3. know when it is appropriate to make a templated function as opposed to a non-templated function

Introduction

In the lesson about function overloading, you learned how you may create multiple functions with the same name that have (presumably related but) different functionality. Now you will learn how to create a function that will work for different types of parameters but have exactly the same functionality. You will create a function template from which template functions are created.

A function template is just exactly that: a template from which the compiler will build different instances of the same function, but they are built to service different parameter types. Thus, the functionality is not changing for the different types.

Function template code cannot be compiled since the types of the variables are not specified. So, this code must be placed

Also note that template functions are NOT prototyped.

Let’s take a look at an example and then discuss it in a little more detail.

template <typename T>
void swap ( T & t1, T & t2)
{
    T temp = t1;
    t1 = t2;
    t2 = temp;
    return;
}

There are two new reserved words in this sample of the syntax, “template” and “typename”. The line that begins with “template” tells the compiler that the code to follow is indeed a function template. <typename T> tells the compiler that the identifier T will be used in place of the typename in this code. The use of ‘T’ as the typename parameter is just a convention; use upper case letters near the end of the alphabet. Any identifier will work, but you will quite often see T or U or S or …. I’ll use different identifiers in examples below just to demonstrate. The remainder of the code is the same as before except that the types of variables have been replaced with T. If we had wanted to use two different types in this template, then we would have employed a comma delimited list, e.g. template <typename T, typename U>. But in this example, we only need one type parameter.

How does this work? Every time we call this function with a previously unused type of parameter, the compiler will copy this code replacing T with that type, then compile the code. To continue the example, suppose we have this main function

int main()
{
    int a = 8, b = 3;
    float c = 4.3, d = 98.5;
    char c1 ='y', c2 = '@';

    swap ( a, b );
    swap ( c1, c2 );
    swap ( c, d );
    return 0;
}

During compiling, when the first swap call is encountered, the compiler notes that both arguments sent are ints. The compiler recognizes that there are no swap functions in its function table that take two ints. It then turns to the template seeing that this swap has two parameters of the same type name. It will then copy the template code replacing all instances of T, the template parameter, with int. Then it will compile the function. When the compiler reaches the second call to swap, it repeats the whole process again. And again, it will repeat with the third call. Thus you see that you will end up with multiple copies of the very same function. (This is sometimes referred to as “code bloat”.) All of this is to save you the time to reproduce this function for all the different types.

There is one thing that you must be very careful of when creating function templates. You must document the function adequately. Why is this so important for templates? This will become much more transparent later in the lessons when you learn how to create your own types. To make it clear now, I will have to use an example you may not fully understand.

template <typename U>
void print (const U u)
{
    cout<<u;
    return;
}

This is a very simple template: it outputs to the screen what I send to the function. However, if I send this function an array (I know, you don’t know what that is), it will fail! The << operator (called the “insertion operator”) is not defined for arrays.

So what is the lesson to be learned here? You MUST document function templates to alert the user of any operations forbidden for certain types. For the above function, I would have the following:

// Pre: The type to fill the template parameter must have the insertion operator
// defined for it.

This function will work fine for all the primitive, built-in types in C++ that you know, but it won’t work for arrays.

// Pre: The type of the template parameter must have the < operator defined.
template <typename T_type>
T_type min_value (const T_type t1, const T_type t2)
{
    return (t1 < t2 ? t1 : t2);
}

// Pre: Both template types must have insertion operator defined.
template <typename T, typename U>
void repeater (const int num_times, const T t1, const U u1)
{
    for (short i = num_times; i > 0; i--)
        cout<<t1<<" "<<u1<<endl;
    return;
}

A typical call:

char some_character = ‘$’;
float a_float_value = 2.2;
repeater(4, some_character, a_float_value);

produces this output:

$ 2.2
$ 2.2
$ 2.2
$ 2.2

Once again, the use of the operators in these functions would disallow the instantiation of the template parameters with arrays and any other user-defined types (covered later in this course) which don’t have the required operators defined.

Course Menu
0. Getting Started
0.0. Welcome
1. The Big Picture
2. Programming Fundamentals
3. Operators
4. Branching
5. Loops
6. Advanced Branching
7. Odds and Ends
8. Functions
9. Random Number Generation
10. Multiple Files
11. Arrays
12. Structs
13. Character Arrays
14. File I/O
15. Objects
16. Output Formatting
16.0. Overview
17. Namespaces
18. Enumerations
19. Sample Homework