Function Templates


Consider the swap function

void swap(int& x, int& y)
{
    int temp = x;
    x = y;
    temp = y;
}
This works only for integer arguments. You need a different version of swap for each data type for which you want to swap elements. If you inspect the version for doubles:

void swap(double& x, double& y)
{
   double temp = x;
   x = y;
   temp = y;
}
you'll notice that the only change was to substitute double for int in the text. This suggests a macro solution, with the data type as a parameter:

// genswap.h
#define genswap(T) void swap(T& x, T& y) \
{                                        \
   T temp = x;                           \
   x = y;                                \
   y = temp;                             \
}
To generate different versions of swap, call genswap as needed:

#include <iostream.h>
#include "genswap.h"

genswap(int)    // Awkward syntax,
genswap(double) // I'll admit.

main()
{
    int i = 1, j = 2;
    double x = 1.1, y = 2.2;
    swap(i,j);
    swap(x,y);
    cout << i << ',' << j << endl; // 2,1
    cout << x << ',' << y << endl; // 2.2,1.1
    return 0;
}
This has the advantage of allowing you to specify the function logic only once.

A function template is much the same as the genswap macro, except that you don't have to explicitly generate the functions you need. After seeing the template definition

template<class T>
void swap(T& x, T& y)
{
    T temp = x;
    x = y;
    y = temp;
}
the compiler automatically generates versions as needed when it finds a call to swap. (See Listing 1. ) You can use any type, including built-ins, for the template argument.

Class Templates

You can also parameterize class definitions. A good candidate is a stack, since the logic of stack operations is the same no matter what type the stack elements are. Listing 2 and Listing 3 have the definition of an integer stack class. To templatize this class, precede the class definition with the line

template<class T>
as before, and change all occurrences of int that refer to the type of elements on the stack to T (see Listing 4) . You instantiate a specific stack class like this:

Stack<int> s1(5);
The type of s1 is Stack<int> ("stack of int"). The token Stack cannot appear unqualified outside of the class template definition. See Listing 5 for an example of using Stack template classes. (Point of Terminology: A "class template" is the original template definition. A "template class" is a particular class instantiated from the class template, such as Stack<int>.)

Note that there is no separate stack2.cpp file. My compilers (Borland 3.1 and WATCOM 9.5) require the entire class implementation to be visible during compilation, so everything is in an include file.

A class template can also have value parameters. The bits class in this article is a good example:

template<size_t N>
class bits
{
     //...
};
The value for N must be a constant expression when instantiated:

bits<16>b1;   // ok
const size_t n = 32;
bits<n> b2;   // ok
size_t m = 64;
bits<m> b3;   // nope - m not const
Since the specific values of N in a program are known at compile time, the array inside a bits<N> object can be placed on the stack, thus avoiding the need for dynamic memory management.

A disadvantage of value parameters occurs with friend functions. Consider the friend function operator& defined in the bits class. To define this outside of the class definition itself, you would have to write:

template<size_t N>
bits<N> operator&(const bits<N>& b1, const bits<N>& b2)
{
     //...
}
Since this is not a member function, the compiler recognizes it as a function template definition. Under the current definition of the language, global function templates can only have type arguments (e.g., class T), because a compiler uses overloading rules to resolve them. That's why I had to fully define all friends inside of the bits<N> class template definition. The joint C++ standards committee voted in November to allow out-of-line definitions of friend functions for class templates.