S R T B H P N

Generic Programming with Templates

Synopsis:

Templates support building parameterized types and functions that accept an unspecified type which gets resolved only when an application instantiates the code with a concrete type. Parameterized code will accept, without compilation error, arbitrary calls on instances of the template type. When an application instantiates the parameterized code with a concrete type compilation of the instantiated code succeeds if the type supports specified operations, else it fails. So there are two C++ template compilation phases:
  1. Compilation of the template library code does a syntax check to identify known errors. No object code is generated since the type of the template parameter is not specified.
  2. Compilation of the instantiated application code generates an object file if instantiated syntax is correct, otherwise it fails.
This lazy syntax checking is very useful. C# and Java generics do eagar type checking so many operations that would succeed for useful types are not allowed because the compiler can't ensure that they will succeed. An implication of this lazy compilation is that all of the template definition code must go in a header file, because the application that uses the template definition has to see all of it's code to compile successfully, and it gets that only by including a header file.

An Example:

The stack<T> class provides a LIFO container for any type, provided it has appropriate copy and assignment operations. It works for all of the native numerical types without writing any additional code.

#ifndef STACK_H
#define STACK_H
///////////////////////////////////////////////////////////////////////
// Stack.h - stack class derived from Effective C++, Scott Meyers    //
//   Note: inclusion model for templates requires all template       //
//         implementations be placed in header file.                 //
// Jim Fawcett, CSE687 - Object Oriented Design, Spring 2004         //
///////////////////////////////////////////////////////////////////////

template<class T> class stack {
private:
  template <class U> friend class stack;
  struct stacknode {
    T data;
    stacknode *next;
    stacknode(const T& newdata, stacknode *nextnode)
      : data(newdata), next(nextnode) { }
  };
  stacknode *top;
public:
  stack();
  ~stack();
  void push(const T& object);
  T pop(void);
  void flush();
  int size() const;
  // member templates
  template <class U> stack(const stack<U>&);
  template <class U> stack<T>& operator=(const stack<U>&);
};

//----< void constructor >---------------------------------------------
template<class T> stack<T>::stack() : top(0) { }

//----< destructor >---------------------------------------------------
template <class T> stack<T>::~stack(void) {
  while (top) {
    stacknode *next_to_die = top;
    top = top->next;
    delete next_to_die;
  }
}
//----< push data onto stack >-----------------------------------------
template<class T> void stack<T>::push(const T &object) {
  top = new stacknode(object, top);
}
//----< pop data from stack >------------------------------------------
template <class T> T stack<T>::pop(void) {
  if (!top) {
    throw std::out_of_range("\n  attempt to pop empty stack\n");
  }
  stacknode *save = top;
  top = top->next;
  T data = save->data;
  delete save;
  return data;
}

Remaining members elided.
            
User defined types have to provide copy constructors and copy assignment operators. You can find an example of that in the TemplateDemo sample code, below. You will also find code for Convert<T> that converts T to and from a string representation. There is also a TemplatesIntro<T> class that shows how Convert<T> is used. We are using the conversion provided by the std::istringstream class extraction operator>> in Check<T>::fromString(const std::string& str), and by the std::ostringstream class insertion operator<< in Check<T>::toString(const T& t). You can do the same for your user-defined classes, simply by overloading those operators to save and retrieve the class's state. For classes with complex state you may need to save to an XML string for insertion, and parse the string for extraction. The XmlDocument package will be helpful for that.

Complete Template Stack Example

Templates Presentation

Templates presentation
Covers most of the important parts of templates including specialization. You want to look at this presentation carefully and concurrently look at code in the repositories cited below.

More Templates Code Examples:

CppBasicDemos, CppUtilities

References: