Any class (template or otherwise) may have a member that is itself a class or function template. Such members are referred to as member templates. One example of a member template is the assign member of the standard containers. The version assign that takes two iterators uses a template parameter to represent the type of its iterator parameters. Another member template example is the container constructor that takes two iterators. This constructor and the assign member allow containers to be built from sequences of different but compatible element types and/or different container types.
Consider the Queue copy constructor: It takes a single parameter that is a reference to a Queue . If we wanted to create a Queue by copying elements from a vector, we could not do so; there is no conversion from vector to Queue . Similarly, if we wanted to copy elements from a Queue into a Queue, we could not do so. Again, even though we can convert a short to an int , there is no conversion from Queue to Queue. The same logic applies to the Queue assignment operator, which also takes a parameter of type Queue&.
The problem is that the copy constructor and assignment operator fix both the container and element type. We’d like to define a constructor and an assign member that allow both the container and element type to vary. When we need a parameter type to vary, we need to define a function template. In this case, we’ll define the constructor and assign member to take a pair ofiterators that denote a range in some other sequence. These functions will have a single template type parameter that represents an iterator type.
2. Defining a Member Template
template <class Type> class Queue { public: // construct a Queuefrom a pair of iterators on some sequence template <class It> Queue(It beg, Itend): head(0), tail(0) { copy_elems(beg, end); } // replace current Queue by contents delimited by a pair of iterators template <class Iter> void assign(Iter, Iter); // rest ofQueue class as before private: // version of copy to be used by assign to copy elements fromiteratorrange template <class Iter> void copy_elems(Iter, Iter); }
When we define a member template outside the scope of a class template, we must include both template parameter lists:
template <class T> template <class Iter> voidQueue<T>::assign(Iter beg, Iterend) { destroy(); // remove existing elements in this Queue copy_elems(beg, end); // copy elements from the input range }
When a member template is a member of a class template, then its definition must include the class-template parameters as well as its own template parameters. The class-template parameter list comes first, followed by the member’s own template parameter list. The definition of assign starts with template <class T> template <class Iter>.
3. Member Templates and Instantiation
Like any other member, a member template is instantiated only when it is used in a program. Member templates have two kinds of template parameters: Those that are defined by the class and those defined by the member template itself. The class template parameters are fixed by the type of the object through which the function is called. The template parameters defined by the member act like parameters of ordinary function templates. These parameters are resolved through normal template argument deduction.
short a[4] = { 0, 3, 6, 9 }; // instantiates Queue<int>::Queue(short *, short *) Queue<int> qi(a, a + 4); // copies elements from a into qi vector<int> vi(a, a + 4); // instantiates Queue<int>::assign(vector<int>::iterator, // vector<int>::iterator) qi.assign(vi.begin(), vi.end());
4. The Complete Queue Class
// declaration that Queueis a template needed for friend declaration inQueueItem template <class Type> class Queue; // function template declaration must precede friend declaration inQueueItem template <class T> std::ostream& operator<<(std::ostream&, constQueue<T>&);
template <class Type> class QueueItem { friend class Queue<Type>; // needs access to item and next friend std::ostream& // defined on page 659 operator<< <Type> (std::ostream&, constQueue<Type>&); // private class: no public section QueueItem(constType &t): item(t), next(0) { } Type item; // value stored in this element QueueItem *next; // pointer to next element in the Queue };
template <class Type> class Queue { // needs access to head friend std::ostream& // defined on page 659 operator<< <Type> (std::ostream&, constQueue<Type>&); public: // empty Queue Queue(): head(0), tail(0) { } // construct a Queuefrom a pair of iterators on some sequence template <class It> Queue(It beg, Itend): head(0), tail(0) { copy_elems(beg, end); } // copy control to manage pointers to QueueItemsin the Queue Queue(constQueue &Q): head(0), tail(0) { copy_elems(Q); } Queue& operator=(constQueue&); // left as exercise for the reader ~Queue() { destroy(); } // replace current Queue by contents delimited by a pair of iterators template <class Iter> void assign(Iter, Iter); // return element from head ofQueue // unchecked operation: front on an empty Queueis undefined Type& front() { return head->item; } constType &front() const { return head->item; } void push(constType &);// defined on page 652 void pop(); // defined on page 651 bool empty() const { // trueif no elements in the Queue return head == 0; } private: QueueItem<Type> *head; // pointer to first element inQueue QueueItem<Type> *tail; // pointer to last element inQueue // utility functions used by copy constructor, assignment, and destructor void destroy(); // defined on page 651 void copy_elems(constQueue&); // defined on page 652 // version of copy to be used by assign to copy elements fromiteratorrange // defined on page 662 template <class Iter> void copy_elems(Iter, Iter); };
// InclusionCompilationModel: include member function definitions as well #include "Queue.cc"
5. static Members of Class Templates
template <class T> class Foo { public: static std::size_t count() { return ctr; } // other interface members private: static std::size_t ctr; // other implementation members };
// Eachobject shares the same Foo<int>::ctrand Foo<int>::count members Foo<int> fi, fi2, fi3;
// has static members Foo<string>::ctrand Foo<string>::count Foo<string> fs;
As with any other static data member, there must be a definition for the data member that appears outside the class. In the case of a class template static , the member definition must inidicate that it is for a class template: