6.3 Variable and Constant Template Parameters


Template parameters may be variables or constant values, including a constant class name. When a variable is used as a template parameter, a value for the variable must be supplied when the template is elaborated. A template may also have both a class parameter and a variable parameter. A constant template parameter is used to define "special case" templates. A special case template is often used in conjunction with a general template to define a special variant of the general template. A special variant is often needed because there are a few cases where the assumption made by the general template are not appropriate and an alternative template is needed. Both of these kinds of template parameters are described below.

Variables as Template Parameters

An example of a variable as a template parameter is shown in the following redefinition of the Queue template. Notice that the template has two parameters: QueueItem that defines the type of the elements maintained by the queue, and size that defines the maximum number of elements that can be in the queue at any one time.
   template <class QueueItem, int size> class Queue {
   private:
      QueueItem buffer[size];
      int head, tail, count;
    public:
                Queue();
      void      Insert(QueueItem item);
      QueueItem Remove(); 
      int       Size();
               ~Queue();
   };

Both template parameters are used in the declaration of the buffer array:
      QueueItem buffer[size];
QueueItem defines the base type of the array and size defines the length of the array. The size template parameter is used in the code for the template as shown below. Notice that the full list of template arguments must be given at numerous places in the definition.

   template <class QueueItem, int size>           
      Queue<QueueItem,size>::Queue() : count(0), head(0), tail(0) {}

   template <class QueueItem, int size> 
      void Queue<QueueItem, size>::Insert(QueueItem item) {
             assert(count <size);
             buffer[tail] = item;
             tail = (tail + 1)% size;
             count++;
      }

   template <class QueueItem, size> 
      QueueItem Queue<QueueItem, size>::Remove() {
            assert(count > 0);
            int val = head;
            head = (head + 1)%size;
            count--;
            return buffer[val];
      }

   template <class QueueItem, int size> 
      int Queue<QueueItem, size>::Size() { return count; }

   template <class QueueItem, int size>           
      Queue<QueueItem, size>::~Queue() {}


Notice that the size parameter is used whenever the array length is needed. Thus instead of "head = (head + 1) % 100;" the code now uses the template variable size to refer to the length of the buffer as in "head = (head + 1) % size;".

Queues of various types and size can be declared and used as follows:


	Queue<int,100>       smallIntegerQueue;
        Queue<int, 1000>     largeIntegerQueue;
	Queue<float, 100>    smallRealQueue;
	Queue<float, 1000>   largeRealQueue;

	smallIntegerQueue.Add(10);
	largeIntegerQueue.Add(20);
	smallRealQueue.Add(1.4159);
        largeRealQueue.Add(0.123);
The type of an object created via a template depends on all of the template's parameters. It was previous seen that instantiating the template different types created objects of different types. Thus, using the previous declaration of the queue template:
	Queue<int>   intQueue1;
        Queue<int>   intQueue2;
        Queue<float> realQueue;
created objects of two different types. As usual, objects of the same type can be assigned while objects of different types cannot. This is shown in the following code:
	intQueue1 = intQueue2;		// OK, queues of the same type

	intQueue1 = realQueue;		// ERROR - queues of incompatible type

	realQueue = intQueue2;		// ERROR - queues of incompatible type

The type compatiblilty of object created by templates with multiple parameters is similar: two template instantiations are the same if and only if they are instantiated with the same parameters. This means that any class parameters must be the same and any variable paramters must have the same value. These rules are illustrated in the following code using the modified (two parameter) Queue template:
	Queue	largeIntegerQueue1;
	Queue	largeIntegerQueue2;
	Queue	smallIntegerQueue;
	Queue	largeRealQueue;
     
	largeIntegerQueue1 = largeIntegerQueue2; // OK, queues of the same type

	largeIntegerQueue1 = largeRealQueue;	 // ERROR - queues of incompatible type
	largeIntegerQueue1 = smallIntegerQueue;	 // ERROR - queues of incompatible type

The first assignment is between compatible types - both the QueueItem type (int) and the size value (100) are the same. However, the second two assignment are in error. The first error occurs because the QueueItem parameters do not agree (int vs. float) although the size parameter is the same (100). The second error occurs becasue the size parameter is different (100 vs. 10) although the QueueItem parameter is the same.

Constants as Template Parameters

There may be certain classes that cannot be used to instantiate a general template because the class does not satisfy the assumptions made by the template about the class given as its parameter. It was seen earlier that the Displayable template made assumptions about its paramterizing class. A portion of the definition of the Displable template is repeated here:
   template <class T> class Displayable {
     private:
       T*       displayed;
       TextBox* textBox;
     public:
       Displayable(TextBox* tbox);        // testbox to show in
       void ShowThis(T* d);		  // what to show
       void Show();                       // show current value
       void Reset();			  // reset displayed from textBox
       ~Displayable();
   };

   ...

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

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

   
The important thing to notice is that the Displayable template assumes that the instantiating class has methods ToString and FromString that convert between the class' internal representation of its value and an external string representation.

Certain obvious uses of the Displayable template will not work because the instantiating class (or type) does not have the methods required by the template. For example:

	Displayable      intDisplay;
	Displayable    realDisplay;
        Displayable locationDisplay;
Will not work because none of these build-in types has the methods required by the Displayable template. While it might be possible to add the required methods to the Location class, it is not possible to do this for the built-in types int and float.

A "special case" version of the Displayable template can be defined for ints as follows:


   class Displayable {
      private:
        int*       displayed;
        TextBox* textBox;
        char* ToString(int v);
        int   FromString(char* text);
      public:
        Displayable(TextBox* tbox);        // testbox to show in
        void ShowThis(int* d);		   // what to show
        void Show();                       // show current value
        void Reset();			   // reset displayed from textBox
        ~Displayable();
   };


   char* Displayable::ToString(int v) { 
      char* buf = new char[10];
      sprintf(buf, "%d", v);
      return buf;
   }

   int Displayable::FromString(char* text) { return atoi(text); }

   Displayable::Displayable(TextBox *tbox)
         {textBox = tbox;  displayed = (int*)0;}

   void Displayable::ShowThis(int* d) { displayed = d; }

   void Displayable::Show() { textBox->SetText(ToString(*displayed)); }

   void Displayable::Reset() { *displayed = FromString(textBox->GetText()); }

   Displayable::~Displayable(){}

Exercises

  1. Extend the IntArray template developed in the exercise in Section 5.1 to include a template argument that defines the length of the array.

  2. Write a program that instantiates three IntArray objects: two of the same size and one of a different size. Show that you can assign and retrieve values at different array positions in each object.

  3. Using the three objects created above, try assigning each one to the other two. Which assignments are valid (will compile) and while produce error messages?
Last Updated: November 7, 1995 / kafura@cs.vt.edu