16.2.10 Templates
Templates--known in other languages as generic types---permit
you to write C++ classes which represent parameterized data types. A
common application for class templates is container classes.
That is, classes which implement data structures that can contain data
of any type. For instance, a well-implemented binary tree is not
interested in the type of data in its nodes. Templates have undergone a
number of changes since their initial inclusion in the ARM. They are a
particularly troublesome C++ language element in that it is difficult to
implement templates well in a C++ compiler.
Here is a fictitious and overly simplistic C++ class template that
implements a fixed-sized stack. It provides a pair of methods for
setting (and getting) the element at the bottom of the stack. It uses
the modern C++ template syntax, including the new typename
keyword (see section 16.2.8 The typename Keyword).
|
template <typename T> class Stack
{
public:
T first () { return stack[9]; }
void set_first (T t) { stack[9] = t; }
private:
T stack[10];
};
|
C++ permits this class to be instantiated for any type you like, using
calling code that looks something like this:
| int
main ()
{
Stack<int> s;
s.set_first (7);
cout << s.first () << endl;
return 0;
}
|
An old trick for fashioning class templates is to use the C
preprocessor. Here is our limited Stack class, rewritten to
avoid C++ templates:
|
#define Stack(T) \
class Stack__##T##__LINE__ \
{ \
public: \
T first () { return stack[0]; } \
void set_first (T t) { stack[0] = t; } \
\
private: \
T stack[10]; \
}
|
There is a couple of subtleties being used here that should be
highlighted. This generic class declaration uses the C preprocessor
operator `##' to generate a type name which is unique amongst
stacks of any type. The __LINE__ macro is defined by the
preprocessor and is used here to maintain unique names when the template
is instantiated multiple times. The trailing semicolon that must follow
a class declaration has been omitted from the macro.
| int
main ()
{
Stack (int) s;
s.set_first (7);
cout << s.first () << endl;
return 0;
}
|
The syntax for instantiating a Stack is slightly different to
modern C++, but it does work relatively well, since the C++ compiler
still applies type checking after the preprocessor has expanded the
macro. The main problem is that unless you go to great lengths, the
generated type name (such as Stack__int ) could collide with other
instances of the same type in the program.
|