Let’s use the Stack
class from Chapter 10 as a model from which to build a template. Here’s the original class declaration:
typedef unsigned long Item;
class Stack
{
private:
enum {MAX = 10}; // constant specific to class
Item items[MAX]; // holds stack items
int top; // index for top stack item
public:
Stack();
bool isempty() const;
bool isfull() const;
// push() returns false if stack already is full, true otherwise
bool push(const Item & item); // add item to stack
// pop() returns false if stack already is empty, true otherwise
bool pop(Item & item); // pop top into item
};
The template approach will replace the Stack
definition with a template definition and the Stack
member functions with template member functions. As with template functions, you preface a template class with code that has the following form:
template <class Type>
The keyword template
informs the compiler that you’re about to define a template. The part in angle brackets is analogous to an argument list to a function. You can think of the keyword class
as serving as a type name for a variable that accepts a type as a value, and of Type
as representing a name for this variable.
Using class
here doesn’t mean that Type
must be a class; it just means that Type
serves as a generic type specifier for which a real type will be substituted when the template is used. Newer C++ implementations allow you to use the less confusing keyword typename
instead of class
in this context:
template <typename Type> // newer choice
You can use your choice of generic type name in the Type
position; the name rules are the same as those for any other identifier. Popular choices include T
and Type
; in this case, we’ll use the latter. When a template is invoked, Type
will be replaced with a specific type value, such as int
or string
. Within the template definition, you can use the generic type name to identify the type to be stored in the stack. For the Stack
case, that would mean using Type
wherever the old declaration formerly used the typedef
identifier Item
. For example,
Item items[MAX]; // holds stack items
becomes the following:
Type items[MAX]; // holds stack items
Similarly, you can replace the class methods of the original class with template member functions. Each function heading will be prefaced with the same template announcement:
template <class Type>
Again, you should replace the typedef
identifier Item
with the generic type name Type
. One more change is that you need to change the class qualifier from Stack::
to Stack<Type>::
. For example,
bool Stack::push(const Item & item)
{
...
}
becomes the following:
template <class Type> // or template <typename Type>
bool Stack<Type>::push(const Type & item)
{
...
}
If you define a method within the class declaration (an inline definition), you can omit the template preface and the class qualifier.
Listing 14.13 shows the combined class and member function templates. It’s important to realize that these templates are not class and member function definitions. Rather, they are instructions to the C++ compiler about how to generate class and member function definitions. A particular actualization of a template, such as a stack class for handling string
objects, is called an instantiation or a specialization. Placing the template member functions in a separate implementation file won’t work. (For a while, the standard did provide the keyword export
to allow such a separate implementation file. But not many vendors implemented it. C++11 discontinues that use of export
but reserves the export
keyword for possible future use.) Because the templates aren’t functions, they can’t be compiled separately. Templates have to be used in conjunction with requests for particular instantiations of templates. The simplest way to make this work is to place all the template information in a header file and to include the header file in the file that will use the templates.
// stacktp.h -- a stack template
#ifndef STACKTP_H_
#define STACKTP_H_
template <class Type>
class Stack
{
private:
enum {MAX = 10}; // constant specific to class
Type items[MAX]; // holds stack items
int top; // index for top stack item
public:
Stack();
bool isempty();
bool isfull();
bool push(const Type & item); // add item to stack
bool pop(Type & item); // pop top into item
};
template <class Type>
Stack<Type>::Stack()
{
top = 0;
}
template <class Type>
bool Stack<Type>::isempty()
{
return top == 0;
}
template <class Type>
bool Stack<Type>::isfull()
{
return top == MAX;
}
template <class Type>
bool Stack<Type>::push(const Type & item)
{
if (top < MAX)
{
items[top++] = item;
return true;
}
else
return false;
}
template <class Type>
bool Stack<Type>::pop(Type & item)
{
if (top > 0)
{
item = items[--top];
return true;
}
else
return false;
}
#endif
3.21.248.162