Lab 09

Templates

Templates are a way of generalizing code in C++. They allow code to be written without consideration of the data type with which it will eventually be used. This is particularly useful in storage structures, such as lists, stacks, queues, etc. So instead of having a whole stack implementation for holding integers, and then another implementation for holding strings, you can create one stack implementation that can be instantiated multiple times to hold different data structures. The compiler decides at compile time from your code what variable type the templated class is working with and instantiates it as such.

This example goes over creating a templated version of a class with only parameter T. More complicated templates using a larger number of parameters can be defined in a similar manner.

Steps to converting a class to a class template

1. Template declaration

The class needs to be explicitly declared a class template. So right above your class declaration add the line:


  template <class T>

This indicates that the following class will be a class template. The ‘class’ in this declaration is associated with the T and not specifically the class template. The T is the type parameter, which will represent the variable type eventually used when the class template is instantiated.

Note: You could really name this type parameter, T, anything you want. It could be called ‘TemplateClass’ or ‘TC’, or whatever. Just usually you will see it named ‘T’, so that is what we will use.

2. Replace old type with type parameter

Go through your header file and every time you see the old variable type that is being generalized to the type parameter, replace it with T.

For example, if the old variable type is typedefed to be ‘StackItemType’, every time you see StackItemType, replace it with ‘T’. Then you can remove the typedef line.

3. Convert class instance declarations to class template instance declarations

Sorry for the confusing title, really this is easy. Your new templated class will be called and created in a slightly different manner from how the original class was created. It will include a definition of what T should be (we will see this later).

But for now, all we need to do is add a <T> to places where the class is dealing with itself. In a common case, this is in the copy constructor.


  Stack(const Stack & aStack);

becomes


  Stack(const Stack<T> & aStack);

Do this in your .cpp file as well.

4. Convert methods to template methods

Go into your implementation file. Now each method definition should contain the full prefix for the class template. This includes the template declaration as well as the type parameter that will become part of the class declaration.

Also, as before in the declarations, modify the old type to the new type parameter – T

So, for example, the push method previously looked like:


  void Stack::push(const StackItemType & newItem) throw(StackException)

Now as a method template it will look like:

  
    template <class T>;
    void Stack<T>::(const T& newItem) throw(StackException)
  

Repeat for all methods.

5. Replace old type with type parameter in declarations

If there is a place in your .cpp file where you create a temporary variable or a new instance of the old variable type that the class once dealt with, replace it as you did in the header file with T

6. Switch around includes

Templates are supposed to be declared and defined in a single header file. We already have them in two files, so we can switch around the include statements so that the compiler thinks its all in one file.

Remove the #include statement in the .cpp file that includes the header file for this class

Add to the bottom of the header file an #include statement that includes the .cpp file there.

When the compiler compiles this header file, it will see the include statement at the bottom of this file and immediately include the .cpp file into what is seemingly the ‘same file’.

If you have the ‘ifndef /define’ compiler instructions, put the .cpp include statement before the #endif

7. Makefile

You don’t have to create an object file from this class, as we are pretending that the .h and .cpp are all in one file.

8. Using your new template class

To instantiate your template class to an actual object that holds a particular type you would create it like:


  Stack<int> newStack;

Where, the int here can be replaced with the type of variables you want this stack to hold.

Function Objects

A function object overloads the parentheses operator (), allowing it to be called as if it were a function.

This provides a way to define functionality at a higher level of the program and use it to affect data stored at a lower one.

  
    struct PrintFunctor
    {
      std::ostream& out;
      PrintFunctor(std::ostream& o) : out(o) { }
      template <typename T>
      void operator() (T item) { out << item << std::endl; }
    };
  

Here we are using structs, but teh same concept can be used with classes as well. We declare a variable out that will store our output stream, as assigned by the constructor of the struct. The the overloaded operator() is what does the work – and here its just sending the output of item to the filestream it read in from its constructor.