Templates

Type-independent recipes for
function or class generation.

No instantiation of code
(for a function or class) until needed.


Template Functions

The classic example. . .
at various times within a single program,
2 values need to be swapped.

One solution:

 void swap(int & a, int & b) {
   int temp; 

   temp = a;
   a = b;
   b = temp;
 }

This function is needed for more than the int type.
So, overload by different signatures
(and associated, but not shown, definitions):

 void swap(int & a, int & b);

 void swap(double & a, double & b);

 void swap(char & a, char & b);

 void swap(myClass & a, myClass & b);

Overloading gets tedious for this case.
The type changes, but none of the code changes!

Template functions solve this problem.



    template <typename Any>
    void swap(Any & a, Any & b) {
      Any temp;

      temp = a;
      a = b;
      b = temp;
    }

Note: older versions of C++ code began with

 template <class Any>

Note: many programmers prefer to use T instead of Any


A use of the template function,

  int x, y;
  swap(x, y);

causes a creation (instantiation) of the code:

  void swap(int & a, int & b) {
    int temp;

    temp = a;
    a = b;
    b = temp;
  }

Yes, if we have the code,

  myClass x, y;
  swap(x, y);

this creates (instantiates) another
version of the code:

  void swap(myClass & a, myClass & b) {
    myClass temp;

    temp = a;
    a = b;
    b = temp;
  }

Hopefully, we all realize that the amount of code
created is no worse than writing
all those overloaded functions for each
needed type.

A benefit of template functions is that
if a new type must be accomodated,
a recompilation instantiates the new code,
without writing more code!


A class can be specified by a template.

The classic examples are container classes.

For example: a stack

Items that we will put into a stack are
all of the same type.
But we may want several stacks, and each stack
may hold items of a different type.

Note that the code is the same,
except for the type of the item that the stack holds,
for each of these stacks.


A parameterized type is "passed" to the template
as an "argument."

Part of the template class declaration:


template <class T>
class Stack {
  public:
    bool push(const T & item);
    bool pop(T & item);
  private:
    T arr[MAX];
    int tos;
}

Just one of the member function definitions:


template <class T>
bool Stack::push(const T & item) {

  if ( tos < MAX ) {
    arr[tos++] = item;
    return true;
  } else {
    return false;
  }
}

Typically: the entire template class
declaration and definition go into the header file.

If we want the definition in a separate file,
then each definition begins with
(constructor example)

export template <class T>
Stack::Stack()

Note: only newer C++ compilers incorporate
the export syntax.


Using (causing the instantiation)
of a Stack < > class.

#include  "stacktemplate.h"
  .
  .
  Stack<int>  my_stack1;

This incorporates the code,
with T replaced by int.

Another instance of the entire class
is made due to the declaration:

  Stack<cookies>  party;

Extra arguments to the template class
are allowed.

A portion of an example where one of these
non-type arguments or expression arguments
is the size of the "container" class:

template <class T, int n>
class Container {
  public:
    .
    .
  private:
    T items[n];
    .
    .
}

A declaration which instantiates the class:

  Container<double, 16>  my_container;

Limitations on expression arguments:


Not enough time to present:

Inheritance and Templates:


Not enough time to present:

We can nest template instantiations:

Stack < Stack <int> >  stackofstacks;

This asks for a stack,
where each element pushed is a stack of ints.


Not enough time to present:

We can have
more than 1 class/typename parameter:

template <class T1, class T2> 
class TwiceTheFun {
  public:
  private:
    T1 a;  // a field may use the type
    T2 b;
}

We may use the 2 parameters independently.


Not enough time to present:

We can provide
a default for a class/typename parameter:

template <class T1, class T2 = int> 
class TwiceTheFun {
  public:
  private:
    T1 a;  // a field may use the type
    T2 b;
}

Specializations

Defines which class is instantiated/used
when multiple possibilities exist.

All examples shown so far (such as)

  Stack <int>  my_stack1;
are implicit instantiations.

Recall that a template instantiation
causes a type-specific version of the
code to be created.

If we want to explicitly ask for
a type-specific version of the code to exist:

  template class Stack<double>;

This is an explicit instantiation.


Use an explicit specialization,
if different behavior is needed for different
parameter types.

  template <typename T>
  const T & min(const T & x, const T & y) {
    return (x < y) ? x : y;
  }

  template <>
  const double & min<double>(const double & x,
                     const double & y) {
    // do something special for doubles
  }

Not enough time to present:



Copyright © Karen Miller, 2007