Professional Documents
Culture Documents
This paper introduces templates, the C++ facility for implementing generic classes and
functions. It also introduces how to use templates to implement container and iterator
classes. A container is a data structure, such as a list, queue or vector, that holds other
objects. An iterator is a mechanism for visiting each object in a container. This paper
presents alternative implementations for containers and iterators, with particular emphasis
on the approach used in the C++ Standard Template Library (STL).
FUNCTION TEMPLATES
A generic function is a single function that takes the place of many overloaded functions
with nearly identical definitions. In C++, you implement a generic function as a function
template. A function template declaration acts as a virtually unbounded set of overloaded
function declarations.
can replace the declarations for many overloaded functions such as:
In both the template declaration and definition, <class T> is a template parameter list,
which specifies T as a template type parameter.
The keyword class in the template parameter list suggests that T can be a class type
only. Actually, T can be a non-class type (such as int) as well as a class type (such as
string). Standard C++ added the keyword typename as an alternative to the key-
word class in template parameter lists, as in:
Within a template definition, a template type parameter behaves like any other type
name. As with any type name, a template type parameter has a scope:
It’s difficult to explain how templates work without using the terms instantiation and spe-
cialization. Unfortunately, the C++ Standard isn’t very clear about what these terms
mean. Consequently, even “experts” use these terms inconsistently.
A function call that names a function template can specify the template argument explic-
itly. For example,
string s, t;
...
swap<string>(s, t);
Compilers can often deduce the template argument from the argument(s) to a call that
names a function template. In that case, the call need not specify the template argument
explicitly. For example:
int i, j;
string s, t;
...
swap(i, j); // calls swap<int>
swap(s, t); // calls swap<string>
CLASS TEMPLATES
A generic class is a single class that can be adapted to specify similar, yet distinct, types.
A generic class can take the place of many classes with similar names and nearly ident i-
cal definitions. In C++, you implement a generic class as a class template. A class tem-
plate definition acts as a virtually unbounded set of type definitions.
class rational
{
public:
rational(int n = 0, int d = 0);
...
private:
int num, denom;
};
Some applications that perform rational arithmetic may need more than int precision;
others may need less. Implementing rational as a template named rational<T>
provides a more flexible design. A programmer using the template can select any integer
type as the type of the numerator and denominator by substituting that type for the tem-
plate type parameter T. For example, rational<int> is the type name for rational
numbers with int precision, and rational<unsigned long int> is the type
name for rational number with unsigned long int precision.
The class template definition defines a class template named rational<T>. The func-
tion declared in the class has the same name as the class, and so that function is a con-
structor.
rational<int> ri(10);
rational<long int>
is called a template-id:
You can use a template-id anywhere you can use any other class name. For instance,
As with non-template classes, the definition for a member function of a class template
can appear inside the class definition. Here, the constructor is defined inside the class
definition:
The member function definition can also appear outside the class definition, as in:
When it appears outside the class template definition, a member function definition must
begin with a template heading such as:
which must have the same number of type parameters as the heading that begins the class
definition. In addition, the member function name must be a fully-qualified name of the
form:
template-id::function-name
rational<T>(T n = 0, T d = 1);
rational (T n = 0, T d = 1);
A class template can have static data members. If that static data member is used any-
where in the program, its definition must appear outside its class definition. For example:
declares counter as a static data member of widget<T>. The definition for that
counter looks like:
A class template can have members that are types. For example, many STL container
classes have a member type called iterator, declared as:
vector<double>::iterator i;
When it appears outside the class definition, the definition of operator<< has the
form:
A function that’s a friend of a template is itself a template only if it has a function pa-
rameter that depends on a template parameter. For example, the second parameter of
operator<< depends on the class template parameter T, so operator<< is a tem-
plate.
A template argument can be any type name, not just an identifier. For example,
declares names as an object of type list specialized with elements of type char *.
A template argument can even be a template-id. Here,
A template parameter can be a non-type parameter rather than a type parameter, as in:
bits is a class that represents a set of N bits. <size_t N> is the template parameter
list. N is a template non-type parameter.
You can specialize class bits for any value of type size_t (an unsigned integer type).
For example,
bits<32> reg;
declares reg as an object representing a 32-bit set. A non-type parameter must have one
of the following types:
• integral
• enumeration
• pointer to object, function or member
• reference to object or function
declares a class representing a pair of values of types T and U. Using the pair template:
template
<typename T, typename X = size_t>
class vector
{
public:
vector(X, X);
...
};
This example defines a template for a vector with elements of type T and indices of
type X. X has size_t as its default, so that:
defines w as a vector<double, int> with indices in the range -100 to +100, in-
clusive.
FUNCTION PARAMETERS
That is, with either declaration, calls to f look the same. For example,
T x;
...
f(x); // might pass x by value or by reference-to-const
If you were to implement f as a template, how should it pass its parameter? By value, or
by reference-to-const? In answering the question, consider how the template function
will perform when you specialize it for various types, both large and small.
For types where passing by value is more efficient, you can define overloaded functions
with the same name as the template:
void f(char c)
{
...
}
A function call uses a template only if it can’t find an overloaded function that’s a better
match:
char c;
string s;
...
f(c); // calls f(char)
f(s); // calls f<string>(...)
ARGUMENT CONSTRAINTS
The following template appears on the surface to work for any type T:
In reality, it works only for certain types. In particular, it works only for types T such
that:
• you can compare a T to 0 using >
• you can apply unary - to a T
• you can construct one T by copying another T (for the return value)
Such requirements are implied argument constraints. Some languages have a notation
for explicitly specifying the constraints on the template argument(s); C++ does not. This
is both good and bad. Sometimes you can apply a template to types that the author never
imagined. On the other hand, the diagnostics you get when you violate a constraint can
be pretty obscure.
COMPILATION MODELS
The two most common template compilation models are known as the separation model
and the inclusion model:
• Under the separation model, you place the template class definition and inline me m-
ber function definitions in a header (as you do with non-template classes). You place
all other template class member function definitions and static data member defini-
tions in one or more source files.
• Under the inclusion model, you place the template class definition and all of its me m-
ber function definitions (inline and non-inline) and static data member definitions in
the header.
The C++ standard supports both of these models. However, the standard requires that
separately compiled template definitions must be declared with the keyword export.
export was added to the standard late in the process. It may be years before most com-
pilers actually do what the standard says.
CONTAINERS
A container (or collection) is a data structure, such as a list, stack, tree, or vector, that
holds objects of other types. For any types T and U, the code for a given container hold-
ing T objects is nearly identical to the code for the same kind of container holding U ob-
jects. Again, templates can eliminate most of the duplication.
As an example, consider a very simple class for a first-in-first-out (FIFO) queue with
elements of arbitrary type T. The basic queue operations are:
• add an element to the back of the queue
• remove an element from the front of the queue
• test if the queue is empty
There are various ways to implement the basic operations. The following example mim-
ics the design used in the STL.
queue<T> uses a singly-linked list implementation. A queue holds pointers to the first
and last cell in the list. A cell holds an element of type T and a pointer to the next cell.
string s;
queue<string> q;
...
if (!q.empty())
{
s = q.front();
q.pop_front();
}
ITERATION FUNCTIONS
The expression cout << q displays the values in queue q. Each value appears in its
default format preceded by a single space. Unfortunately, queue<T>’s operator<<
is not very flexible in that it does not allow any control over formatting.
operator<< is actually a very specific form of a general function that performs some
operation on each element in the container. You can implement this more general capa-
bility as a member function called for_each. for_each is an iteration function that
applies a specified function to each element in the container. For instance:
q.for_each(put);
The declaration for for_each replaces the declaration for operator<< in the class
definition:
Here’s how you might use for_each to write the strings in a queue formatted so that
each string is enclosed in single quotes and preceded by a space. First, define:
ostream *qos;
This put function writes the single string v enclosed in single quotes and preceded by a
space. You can use put to define operator<< as a non-member:
queue<string> q;
...
cout << q;
ITERATOR CLASSES
Iteration functions can be effective, but tend to be inflexible and inconvenient. Extract-
ing statements from a loop body placing them into a separate function is a somewhat
“unnatural” way to write a loop. Also, passing additional parameters through the itera-
tion function to the applied function is difficult.
Iterator classes offer a more flexible way to apply an operation to each element in a con-
tainer. An iterator class is an abstract type for objects that can locate (“point” to) each
element in a container. Together, the container and its iterator provide functions (not
necessarily distinct) to:
• obtain the “address” of the first element in the container
• “increment” an iterator to “point” to the next element in the container
• determine when there’s no next element to “point” to
An iterator class is actually part of the public interface of its container class. Thus, it’s
customary to declare an iterator class as a public member of its container class. When the
queue’s iterator is a public member of the queue class template, the fully-qualified name
for a queue iterator is queue<T>::iterator.
There are many ways to design the iterator interface. The C++ standard library includes
a number of container class templates known collectively as the STL (Standard Template
Library). The following queue iterator is modeled after an STL iterator.
A loop that does something with each element in q of type queue<string> looks
something like:
queue<string>::iterator p;
p = q.begin();
while (p != q.end())
{
// do something with *p
++p;
}
Members of a class normally have access to the non-public members of that class. A
nested class is a member of its enclosing class, but a nested class does not have access to
the non-public members of its enclosing class. Therefore, the queue<T> class defini-
tion must include the following declarations:
class iterator;
FINAL REMARKS
The template syntax, though occasionally puzzling, is not very difficult to master. Turn-
ing simple non-template functions and classes into templates is fairly easy. However,
writing truly general-purpose templates takes considerable practice.
Templates are easy to use in that template invocations are pretty terse. However, they’re
not so easy when you make a mistake, because your compiler may not give you meaning-
ful diagnostics.
Even though there is a C++ standard, the C++ standards committee will probably be
ironing out problems in templates for years to come. Compiler vendors are scrambling to
catch up to the specification, and finding more problems as they do. For the foreseeable
future, writing portable code using templates will remain difficult.