Templates Templates

The intent: to make our objects work for many different kinds of data types.

So far we have used typedef to do this in a rough way. Another, more powerful (but slower way) is to set up template functions or template classes.

Template Functions

Wouldn't it be nice to have a sort function that will sort any kind of array? The following program has such a sort function. It also has a function that will print out any kind of array.

#include <iostream>
#include <string>

using namespace std;

template <class T>
void sort (<T> array [], int size)
{
   bool isExchange = true;
   
   while (isExchange)
   {
      isExchange = false;
      
      for (int n = 0; n < size - 1; n++)
      {
         if (array [n] > array [n + 1])
         {
            T temp = array [n];
            
            array [n]     = array [n + 1];
            array [n + 1] = temp;
         
            isExchange = true;
         }
      }
   }
}

template <class T>
void outputArray (<T> array [], int size)
{
   for (int n = 0; n < size; n++)
   {
      cout << array [n] << endl;
   }
}

int main ()
{
   int array [10] = {4,7,2,9,1,3,8,6,5};
   
   sort (array, 9);
   
   outputArray (array, 9);
   
   string array2 [10] = {"harvey", "jo", "cathy", "james", "francis",
                         "grace", "jason", "ron", "may", "gene"};
                         
   sort (array2, 10);
   
   outputArray (array2, 10);
   
   return 0;
}

Things to notice:

  1. The functions sort and outputArray are template functions. They begin with the statement template <class T>.

    This means that the function is written in terms of the unknown data type T (we could have called it whatever we want), and the computer will figure out what T is from the arguments we give the function.

  2. In the main program the first call to the sort function uses an int array. The computer uses this information to make the sort an int sort.

    The second call to the sort function uses a string array. The computer uses this information to make the sort a string sort.

  3. Since the data type T is to be determined, you can consider the entire function definition to be the function prototype. This means that many compilers require template functions (the entire function) to come in the same place as other prototypes.

  4. If the data type that will be plugged in for T is a class, then that class needs to have a copy constructor.

  5. If the data type that will be plugged into the sort function is a class, then that class needs to have the > operator overloaded.

Template Classes

A whole class can be built on a variable data type by use of the template idea.

Here is a simple list class. The code (class definition AND functions) should go in a .h file or a .template file, depending on the compiler.

#ifndef LIST_H
#define LIST_H

#include <iostream>

//Note:  using namespace std; is not present here because this file 
//       will be included in main and not in a functions file
//       of its own.  This entire file is a header, including the 
//       functions code, which is incomplete because of the templates.  
//       This file will not compile to anything on its own.

const int MAXSIZE = 100;

template <class T>
class List
{
   public:
      List (T [], int);
      List (List&);
      
      T    operator[] (int);
      
      int  getSize ();
      void sort    ();
   
   private:
      T array [MAXSIZE];
      
      int size;
};

template <class T>
List<T>::List (T anArray [], int howMany)
{
   size = howMany;
   
   for (int n = 0; n < size; n++)
      array [n] = anArray [n];
}

template <class T>
List<T>::List (List& aList)
{
   size = aList.size;
   
   for (int n = 0; n < size; n++)
      array [n] = aList.array [n];
}

template <class T>
T List<T>::operator[] (int n)
{
   return array [n];     //Note:  crashes if n < 0 or n >= size
}

template <class T>
int List<T>::getSize ()
{
   return size;
}

template <class T>
void List<T>::sort ()
{
   bool isExchanges = true;
   
   while (isExchanges)
   {
      isExchanges = false;
      
      for (int n = 0; n < size - 1; n++)
      {
         if (array [n] > array [n + 1])
         {
            T temp = array [n];
            
            array [n]     = array [n + 1];
            array [n + 1] = temp;
            
            isExchanges = true;
         }
      }
   }
}

template <class T>
ostream& operator<< (ostream& out, List<T>& it)
{
   for (int n = 0; n < it.getSize (); n++)
      out << it [n] << endl;
   
   return out;
}
#endif

Things to notice here are:

  1. There is no file of functions of the class. The functions are part of the class definition because of the template nature of the class and functions.
  2. template <class T> precedes the class definition. Thereafter T is a stand in for whatever the data type will get plugged into the class.
  3. template <class T> precedes each function definition.

A main program (in the file main.cpp) might look like this:

#include <iostream>
#include <string>

using namespace std;

#include "listTemplate.h"

int main ()
{
   int array [9] = {4,6,8,2,1,5,3,9,7};
   
   List<int> aList (array, 9);
   
   aList.sort ();
   
   cout << aList << endl;
   
   string array2 [10] = {"harvey", "jo", "cathy", "james", "francis",
                         "grace", "jason", "ron", "may", "gene"};

   List<string> bList (array2, 10);
   
   bList.sort ();
   
   cout << bList << endl;
   
   return 0;
}

Things to notice here:

  1. The List object is declared with the type that will be plugged in for T in the class definition. Here this is done in the 2 lines:

    List<int> aList (array, 9);
    List<string> bList (array2, 10);
    


File translated from TEX by TTH, version 2.25.
On 7 Oct 2002, 18:03.