7.2 Template Parameters


 

A template class makes assumptions about the operations that the template itself may safely apply to objects of the class defined by the template parameter. Because C++ DOS not provide a means to explicitly state these assumptions, the assumptions are implicit and must be conveyed by the documentation of the template class or inferred by examining the code of the template class. As the assumptions may be subtly imposed, a good template designer will ensure that the documentation for a template explicitly lists all of the requirements that must be met by the template parameter.

The Queue template class makes three assumptions about the QueueItem template parameter.

The first assumption made by the Queue template is in the declaration of the array of QueueItems:

  template <class QueueItem> class Queue {
   private:
      QueueItem buffer[100];
  ...
   };

This declaration assumes that QueueItem has a default constructor (i.e., a constructor that takes no arguments). A default constructor is needed in order to declare an array of a given type. Classes like Location, Shape, and Frame that have default constructors can be used to elaborate the Queue template. However, an error message will result from:

       Queue<Message>  QueueOfMessages;

        Queue<Clock>    QueueOfClocks;
     

because these classes do not have default constructors.

The second assumption made by the Queue template is the assignment of QueueItems:

 template <class QueueItem> 
      void Queue<QueueItem>::Insert(QueueItem item) {
             ...
             buffer[tail] = item;
             ...
       }

This assignment statement assumes that the QueueItem class has an appropriately defined assignment operator. All classes have a default assignment operator - a bit-wise copy. In simple classes, this is an appropriate assignment operator. For example, in the case of Queue<int> and Queue<Location>, the default assignment operator is appropriate. However, the default assignment operator may not be appropriate for a class that contains pointers as private data. For example, the Message class and the Clock class each contain pointers as private data.

The third assumption the Queue template makes is in the way QueueItems are returned by the Remove method:

 template <class QueueItem> 
      QueueItem Queue<QueueItem>::Remove() {
            ...
            return buffer[val];
     }

This code returns a copy of the object in the internal buffer. This code assumes that an appropriate copy constructor is defined for the class being substituted for QueueItem.

Other templates make more demanding assumptions about the class or type used to elaborate the template. Consider this template declaration for a Displayable template:


The Displayable Template

template <class T> class Displayable {
     private:
       T*       displayed;
       TextBox* textBox;
     public:
       Displayable(TextBox* tbox);        // textbox to show in
       void ShowThis(T* d);               // what to show
       void Show();                       // show current value
       void Reset();                      // reset displayed from textBox
       ~Displayable();
   };

 

This template is designed to point to a TextBox in which a current value will be displayed and from which a new value can be read. The template also contains a pointer ("displayed") to an object of the template's parameter type. To achieve its intent, the template must make assumptions about how the value can be obtained from the displayed object and how a new value can be given to the displayed object. Since the type of the displayed object is now known (its type is the template's parameter), the template code must make assumptions about (impose constraints on) the type that is used to elaborate the template. The code for the Displayable template is shown below.


Implementation of the Displayable Template
template <class T> 
      Displayable<T>::Displayable(TextBox *tbox)
         {textBox = tbox;  displayed = (T*)0;}

   template <class T> 
      Displayable<T>::ShowThis(T* d) { displayed = d; }

   template <class T> 
      Displayable<T>::Show() { textBox->SetText(displayed->ToString()); }

   template <class T> 
      Displayable<T>::Reset() { displayed->FromString(textBox->GetText()); }

   template <class T> 
      Displayable<T>::~Displayable(){}

  
   

The implementation of the Displayable template assumes that the instantiating type has two methods:

  • char *ToString() : returns a character string representation of the value of the object, and
  • void FromString(char*) : derives a new value for the object from a character string representation.

Any type satisfying these two assumptions may be used to elaborate the Displayable template. No type or class lacking either of these methods can be used to elaborate the template; for example, it is not possible to have Displayable<int>, Displayable<char*>, or Displayable<Counter> for all of these lack the required methods ToString and FromString.

  1. Write a class named Integer that holds a value of type int. This class should have the ToString and FromString methods defined for it. Write a test program that uses this class to elaborate the Displayable template and display an integer in the TextBox.

  2. Write a class named Real that holds a value of type float. This class should have the ToString and FromString methods defined for it. Write a test program that uses this class to elaborate the Displayable template and display a float value in the TextBox.

 




©1998 Prentice-Hall, Inc.
A Simon & Schuster Company
Upper Saddle River, New Jersey 07458

Legal Statement