Chapter 18: Templates

We're always interested in getting feedback. E-mail us if you like this guide, if you think that important material is omitted, if you encounter errors in the code examples or in the documentation, if you find any typos, or generally just if you feel like e-mailing. Send your email to Frank Brokken.

Please state the document version you're referring to, as found in the title (in this document: 5.2.0a) and please state the paragraph you're referring to.

All mail received is seriously considered, and new (sub)releases of the Annotations will normally reflect your suggestions for improvements. Except for the incidental case I will not otherwise acknowledge the receipt of suggestions for improvements. Please don't misinterpret this for lack of appreciation.

The C++ language support a mechanism which allows programmers to define completely general functions or classes, based on hypothetical arguments or other entities. Code in which this mechanism has been used is found in, e.g., de chapter on abstract containers.

These general functions or classes are used to generate concrete code once their definitions are applied to real entities. Such a general definition of a function or a class is called a template, the concrete code that is generated based on a template is called an instantiation.

In this chapter we will examine template functions and template classes.

18.1: Template functions

Template functions are used in cases where a single implementation of a function is not practical due to the different types that are distinguished in C++. If a function is declared as
    fun(int *array)l
then this function will likely run into problems if it is passed the address of an array of double values. The function will normally have to be duplicated for parameters of different types. For example, a function computing the sum of the elements of an array for an array of ints is:
    int sumVector(int *array, unsigned n)
    {
        int 
            sum(0);        
        for (int idx = 0; idx < n; ++idx)
            sum += array[idx];
        return (sum);
    }        
The function must be overloaded for arrays of doubles:
    double sumVector(double *array, unsigned n)
    {
        double 
            sum(0);        
        for (int idx = 0; idx < n; ++idx)
            sum += array[idx];
        return (sum);
    }        
In a local program development situation this hardly ever happens, since only one or two sumVector() implementations will be required. But the strongly typed nature of C++ stands in the way of creating a truly general function, that can be used for any type of array.

In cases like these, template functions are used to create the truly general function. The template function can be considered a general recipe for constructing a function that can be used with the generic array. In the upcoming sections we'll discuss the construction of template functions. First, the construction of a template function is discussed. Then the instantiation is covered. With template functions the argument deduction deserves special attention. See section 18.1.3 for this topic.

18.1.1: Template function definitions

The definition of a template function is very similar to the definition of a normal function, except for the fact that the parameters, the types that are used in the function, and the function's return value may be specified in a completely general way. The function sumVector() in the previous section can be rewritten as a template function:
    #include <numeric>

    template <typename T>
    T sumVector(T *array, unsigned n)
    {
        return std::accumulate(array, array + n, T());
    }        
Note the correspondence with the formerly defined sumVector() functions. In fact, if a typedef int T had been specified, the template function, except for the initial template line, would be the first sumVector() function of the previous section. So, the essence of the template function is found in the first line. From the above example:
template <typename T>

Definitions or declarations of templates always start with the template <...> line. It is followed by the template parameter list, which is a comma-separated non-empty list of so-called template type or template non-type parameters, surrounded by angular brackets < and >. In the Annotations template type parameters always start with the keyword typename. In other texts on C++ the keyword class can also be encountered. So, in other texts template definitions might start with a line like:

template <class T>

Using class instead of typename is now, however, considered an anachronism, and is deprecated: a template type parameter is, after all, a type name and not a class.

In the template function sumVector() the only template parameter is T, which is a template type parameter. T is the formal type that is used in the template function definition to represent the actual type that will be specified when the template function is instantiated. This type is used in the parameter list of the function, it is used to define the type of a local variable of the function, and it is used to define the return type of the function.

Normal scope rules and identifier rules apply to template definitions and declarations: the type T is a formal name, it could have been named Type. The formal typename that is used overrules, within the scope of the template definition or declaration, any previously defined identifiers by that name.

A template non-type parameter represents a constant expression, which must be known by the time the template is instantiated, and which is specified in terms of existing types, such as an unsigned. An alternative definition for the above template function, using a template non-type parameter is (note that there are two versions: one for arrays having const elements, one for arrays having elements that can be modified):

    #include <numeric>

    template <typename T, unsigned size>       // non-const arrays
    T sumVector(T (&array)[size])
    {
        return std::accumulate(array, array + size, T());
    }        

    template <typename T, unsigned size>       // const arrays
    T sumVector(const T (&array)[size])
    {
        return std::accumulate(array, array + size, T());
    }        
Template function definitions may have multiple type and non-type parameters. Each parameter name must be unique. For example, the following template defines a template function for a function outerProduct(), returning a pointer to vectors of size2 T2 elements, and expecting two vectors of, respectively, size1 and size2 elements:
    template 
    <
        class T1, 
        class T2, 
        unsigned size1, 
        unsigned size2
    >
        T1
        (
            *outerProduct
            (
                T2 const (&v1)[size1], 
                T2 const (&v2)[size2]
            )
        )[size2];
Note that the return type T1 of the returned vectors is intentionally specified different from T2. This allows us to specify, e.g., return type double for the returned outer product, while the vectors passed to outerProduct are of type int.

The keyword typename is required in certain situations that may occur when the template function is defined. For example, assume we define the following template function:

    template <typename T>
    void function()
    {
        T::member
            *p;
    }        
Although the layout of the above function suggests that p is defined as a pointer to the type member, that must have been declared in the class that is specified when the function is instantiated, it actually is interpreted by the compiler as a multiplication of T::member and p.

The compiler does so, because it cannot know from the template definition whether member is a typename, defined in the class T, or a member of the class T. It takes the latter and, consequently, interprets the * as a multiplication operator.

What if this interpretation was not intended? In that case the typename keyword must be used. In the following template definition the * indicates a pointer definition to a T::member type.

    template <typename T>
    void function()
    {
        typename T::member
            *p;
    }        

18.1.2: Instantiations of template functions

Consider the first template function definition in section 18.1.1. This definition is a mere recipe for constructing a particular function. The function is actually constructed once it is used, or its address is taken. Its type is implicitly defined by the nature of its parameters.

For example, in the following code it is assumed that the function sumVector has been defined in the header file sumvector.h. In the function main() the function sumVector() is called once for the int array x, once for the double array y, and once the address is taken of a sumVector() function. By taking the address of a sumVector function the type of the argument is defined by the type of the pointer variable, in this case a pointer to a function processing a array of unsigned long values. Since such a function wasn't available yet (we had functions for ints and doubles), it is constructed once its address is required. Here is the function main():

    #include <iostream>
    #include "sumvector.h"

    using namespace std;
    
    int main()
    {
        int
            x[] = {1, 2};
        double
            y[] = {1.1, 2.2};
    
        cout << sumVector(x, 2) << endl     // first instantiation
            << sumVector(y, 2) << endl;     // second instantiation
    
        unsigned long                       // third instantiation
            (*pf)(unsigned long *, unsigned) = sumVector;
    }
    /*
        Generated output:

        3
        3.3
    */
While in the above example the functions sumVector() could be instantiated, this is not always possible. Consider the following code:
    #include <iostream>
    #include "sumvector.h"

    unsigned fun(unsigned (*f)(unsigned *p, unsigned n));
    double fun(double (*f)(double *p, unsigned n));
    
    int main()
    {
        std::cout << fun(sumVector) << std::endl;
    }
In the above example the function fun() is called in the function main(). Although it appears that the address of the function sumVector() is passed over to the function fun(), there is a slight problem: there are two overloaded versions of the function fun(), and both can be given the address of a function sumVector(). The first function fun() expects an unsigned *, the second one a double *. Which instantiation must be used for sumVector() in the fun(sumVector) expression? This is an ambiguity, which makes the compiler balk. The compiler complains with a message like
    In function `int main()':
    call of overloaded `fun ({unknown type})' is ambiguous
    candidates are: fun(unsigned int (*)(unsigned int *, unsigned int))
                    fun(double (*)(double *, unsigned int))
Situations like this should of course be avoided. Template functions can only be instantiated if this can be done unambiguously. It is, however, possible to disambiguate the situation using a type cast. In the following code fragment the (proper) double * implementation is forced by means of a static_cast:
    #include <iostream>
    #include "sumvector.h"

    unsigned fun(unsigned (*f)(unsigned *p, unsigned n));
    double fun(double (*f)(double *p, unsigned n));
    
    int main()
    {
        std::cout 
            << fun(static_cast<double (*)(double *, unsigned)>(sumVector)) 
            << std::endl;

        return 0;
    }
But type casts should be avoided, where possible. Fortunately the cast can be avoided in this kind of situation, as described in section 18.1.4.

If the same template function definition was included in different source files, which are then compiled to different object files which are thereupon linked together, there will, per type of template function, be only one instantiation of the template function in the final program.

This is illustrated by the following example, in which the address of a function sumVector() for int arrays is written to cout. The first part defines a function fun() in which the address of a sumVector() function is written to cout. The second part defines a function main(), defined in a different sourcefile, in which the address of a similar sumVector() function is written to cout, and in which fun() is called. Both sources use the union PointerUnion to convert a pointer to a function to a void *. Here is the used union:

    union PointerUnion
    {
        int (*fp)(int *, unsigned);
        void *vp;
    };
The source of the function fun():
    #include <iostream>
    #include "sumvector.h"
    #include "pointerunion.h"

    void fun()
    {
        PointerUnion
            pu = { sumVector };

        std::cout << pu.vp << std::endl;
    }
And the code of main():
    #include <iostream>
    #include "sumvector.h"
    #include "pointerunion.h"

    void fun();

    int main()
    {
        fun();

        PointerUnion
            pu = { sumVector };
        
        std::cout << pu.vp << std::endl;

        return 0;
    }
    /*
        Generated output (actual pointer values may be different):
    
        0x8048710
        0x8048710
    */
Note that the program shows the same function addresses for the two instantiations: in the final program only one copy of sumVector() is used.

18.1.2.1: Declaring template functions

Template functions can be declared as well. If it is known that the required instantiation is available from another source file, as the sumVector that was used in the function fun() in the previous section, then there is actually no need to instantiate the function sumVector() again.

A template function declaration is constructed like any other function declaration: by replacing its code with a semicolon. For example:

    #include <iostream>
    #include "sumvector.h"
    #include "pointerunion.h"

    template<typename T>
    T sumVector(T *tp, unsigned n);

    void fun()
    {
        PointerUnion
            pu = { sumVector };

        std::cout << pu.vp << std::endl;
    }
To make this work, one must of course be certain that the instantiation is available elsewhere. The advantage of this approach is that the compiler doesn't have to instantiate a template function, which speeds up the compilation of the function in which the template function is only declared. The disadvantage is that we have to do the bookkeeping ourselves: is the template function used somewhere else or not?

Regarding namespaces: template function definitions should not use using directives or declarations: the template might be used in a situation where such a directive overrides the programmer's intentions: ambiguities or other conflicts may result from the template's author and the programmer using different using directives (E.g, a cout variable defined in the std namespace and in the programmer's own namespace).

18.1.3: Argument deduction

The compiler determines the type of the template function that must be instantiated by examining the types and values of its arguments. This process is called template argument deduction. With template argument deduction, the type of the return value of the template function is not considered.

For example, consider once again the function

T sumVector(T (&array)[size])
given in section 18.1.1:
    #include <numeric>

    template <typename T, unsigned size>       // non-const arrays
    T sumVector(T (&array)[size])
    {
        return std::accumulate(array, array + size, T());
    }        

    template <typename T, unsigned size>       // const arrays
    T sumVector(const T (&array)[size])
    {
        return std::accumulate(array, array + size, T());
    }        
In this function the template non-type parameter size is determined from the size of the array that is used with the call. Since the size of an array is known to the compiler, the compiler can determine the size parameter by looking up the size of the array that is used as argument to the function sumVector(). If the size is not known, e.g., when a pointer to an array element is passed to the function, the compilation will not succeed. Therefore, in the following example, the first call of the function sumVector() will succeed, as iArray is an array; the second one will fail, as iPtr is a pointer, pointing to an array of (in principle) unknown size:
    #include "sumvectorsize.h"

    int main()
    {
        int
            iArray[] = {1, 2, 3},
            *iPtr = iArray;

        sumVector(iArray);  // succeeds: size of iArray is known
        sumVector(iPtr);    // fails: size of array pointed to by 
                            // iPtr is unknown 
    }
It is not necessary for a template function's argument to match exactly the type of the template function's corresponding parameter. Three kinds of conversions are allowed here: These three types of conversions are now discussed and illustrated.

18.1.3.1: Lvalue transformations

There are three types of lvalue transformations:

lvalue-to-rvalue conversions.
An lvalue-to-rvalue conversion takes place in situations where the value of an lvalue expression is required. This also happens when a variable is used as argument to a function having a value parameter.

array-to-pointer conversions.
An array-to-pointer conversion occurs when the name of an array is assign to a pointer variable. This is frequently seen with functions using parameters that are pointer variables. When calling such functions, an array is often specified as argument to the function. The address of the array is then assigned to the pointer-parameter. This is called an array-to-pointer conversion.

function-to-pointer conversions.
This conversion is most often seen with functions defining a parameter which is a pointer to a function. When calling such a function the name of a function may be specified for the parameter which is a pointer to a function. The address of the function is then assigned to the pointer-parameter. This is called a function-to-pointer conversion.
In the first sumVector() template (section 18.1.1) the first parameter is defined as a T *. Here an array-to-pointer conversion is allowed, as it is an lvalue transformation, which is one of the three allowed conversions. Therefore, the name of an array may be passed to this function as its first argument.

18.1.3.2: Qualification conversions

A qualification conversion adds const or volatile qualifications to pointers. Assume the function sumVector() in section 18.1.1 was defined as follows:

    #include <numeric>

    template <typename T>
    T sumVector(T const *array, unsigned n)
    {
        return std::accumulate(array, array + n, T());
    }        
In the above definition, a plain array or pointer to some type can be used in combination with this function sumVector(). E.g., an argument iArray could be defined as int iArray[5]. However, no damage is inflicted on the elements of iArray by the function sumVector(): it explicitly states so, by defining array as a T const *. Qualification conversions are therefore allowed in the process of template argument deduction.

18.1.3.3: Conversion to a base class

In section 18.2 template classes are formally introduced. However, they were already used earlier: abstract containers (covered in chapter 12) are actually defined as template classes. Like `normal' classes, template classes can participate in the construction of class hierarchies. In section 18.2.7 it is shown how a template class can be derived from another template class.

Assume that the template class Pipe is derived from the class queue. Furthermore, assume our function sumVector() was written to return the sum of the elements of a queue:

    template <typename T>
    T sumVector(std::queue<T> &queue)
    {
        T sum();        

        while (!queue.empty())
        {
            sum += queue.front();
            queue.pop();
        }
        return sum;
    }        
All kinds of queue objects can be passed to the above function. However, it is also possible to pass Pipe objects to the function sumVector(): By instantiating the Pipe object, its base class, which is the template class queue, is also instantiated. Now: Consequently, the definition `Pipe<int> pi;' implies the instantiation of the base class queue<int>, which is an allowed type for the first parameter of sumVector(). Therefore, pi may be passed as argument to sumVector().

This conversion is called a conversion to a base class instantiated from a class template. In the above example, the class template is Pipe, the base class is queue.

18.1.3.4: Summary: the template argument deduction algorithm

The following algorithm is used with template argument deduction when a template function is called with one or more arguments:

18.1.4: Explicit arguments

Consider once again the function main() is section 18.1.2. Here the function sumVector() was called as follows:
    #include <iostream>
    #include "sumvector.h"

    using namespace std;

    int main()
    {
        int     x[] = {1, 2};
        double  y[] = {1.1, 2.2};
    
        cout << sumVector(x, 2) << endl
            << sumVector(y, 2) << endl;

        return 0;
    }
In both cases the final argument of the function is of type int, but in the template's definition, the second parameter is an unsigned. The conversion unsigned -> int is not one of the allowed conversions lvalue transformation, qualification conversion or conversion to a base class. Why doesn't the compiler complain in this case? In cases where the type of the argument is fixed, standard type conversions are allowed, and they are applied automatically by the compiler. The types of arguments may also be made explicit by providing type casts. In those cases there is no need for the compiler to deduce the types of the arguments.

In section 18.1.2, a type cast was used to disambiguate. Rather than using a static_cast, the type of the required function can be made explicit using another syntax: the function name may be followed by the types of the arguments, surrounded by pointed brackets. Here is the example of section 18.1.2 using an explicit template argument type:

    #include <iostream>
    #include "sumvector.h"

    unsigned fun(unsigned (*f)(unsigned *p, unsigned n));
    double fun(double (*f)(double *p, unsigned n));
    
    int main(int argc, char **argv)
    {
        std::cout << fun(sumVector<double>) << std::endl;
    }
The explicit argument type list should match the types mentioned in the template<...> line preceding the template's function definition. The type class T in the template line of the function sumVector() is made explicit as type double, and not as, e.g., a type double *, which was used in the static_cast in the example of section 18.1.2.

18.1.4.1: Template explicit instantiation declarations

Templates can be declared, explicitly providing the types of the template's arguments with the purpose of instantiating these templates. Such an explicit instantiation declaration starts with the keyword template, to be followed by an explicit template instantiation declaration. Although this is a declaration (see also section 18.1.2.1), it is also understood by the compiler as a request to instantiate that particular variant of the function.

By using explicit instantiation declarations it is possible to collect all required instantiations of a template functions in one file, so that sources using these template functions can use plain, non-instantiating template function declarations, in order to redure their compilation time. For example, several sumVector() functions can be nstantiated as follows:

    #include "sumvector.h"

    template      int sumVector<int>(int *, unsigned);
    template   double sumVector<double>(double *, unsigned);
    template unsigned sumVector<unsigned>(unsigned *, unsigned);
As seen from this example, explicit instantiation declarations are mere function declarations, e.g.,
int sumVector(int *, unsigned);

embellished with the template keyword and an explicit template argument list, e.g., <int>.

18.1.5: Template explicit specialization

Although the function sumVector() we've seen in the previous sections is well suited for arrays of elements of the basic types (like int, double, etc.), the template implementation is of course not appropriate in cases where the += operator is not defined. In these cases a template explicit specialization may be used.

For example, the template's implementation of sumVector() is not suited for variables of type char *, like the argv parameter of main(). If we want to be able to use sumVector() with variables of type char * as well, we can define the following special form of sumVector():

    #include <iostream>
    #include "specialization.h"

    int main(int argc, char **argv)
    {
        std::cout << sumVector(argv, argc);
    }
A template explicit specialization starts with the keyword template, followed by an empty set of pointed brackets. This is followed by the head of the function, which must follow the same syntax as a template explicit instantiation declaration, albeit that the trailing ; of the declaration is replaced by the actual function body of the specialization implementation. In particular note that the ordering of the template parameters and other function parameters in the explicit specialization must be identical to the ordering used in the orginal template function. So, the template explicit specialization could not use argc and argv in the order used by, e.g., main().

The template explicit specialization is normally included in the same file as the standard implementation of the template function. If the template explicit specialization is to be used in a different file than the file in which it is defined, it must be declared. Of course, being a template function, the definition of the template explicit specialization can also be included in every file in which it is used, but that will also slow down the compilation of those other files.

The declaration of a template explicit specialization obeys the standard syntax of a function declaration: the definition is replaced by a semicolon. Therefore, the declaration of the above template explicit specialization is

template <> char *sumVector<char *>(char **, unsigned);

Note the pair of pointed brackets following the template keyword. Were they omitted, the function would reduce to a template instantiation declaration: you would not notice it, except for the longer compilation time, as using a template instantiation declaration implies an extra instantiation (i.e., compilation) of the function. Furthermore, only one location of the non-template function would be allowed in the sources of a program, so a header file would not exactly be a good location for the code of a non-template function.

In the declaration of the template explicit specialization the explicit specification of the template arguments (in the < ... > list following the name of the function) can be omitted if the types of the arguments can be deduced from the types of the arguments. With the above declaration this is the case. Therefore, the declaration can be simplified to:

template <> char *sumVector(char **, unsigned);

Comparably, the template <> part of the template explicit specialization may be omitted. The result is an ordinary function or ordinary function declaration. This is not an error: template functions and non-template functions may overload each other. Ordinary functions are less restrictive in the type conversions that are allowed for their arguments than template functions, which might be a reason for using an ordinary function.

18.1.6: Overloading template functions

Template functions may be overloaded. The function sumVector() defined earlier (e.g. in section 18.1.1) may be overloaded to accept, e.g., variables of type vector:
    #include <vector>
    #include <numeric>

    template <typename T>
    T sumVector(std::vector<T> &array)
    {
        return std::accumulate(array.begin(), array.end(), T());
    }        
Such a template function can be used by passing it an argument of type vector, as in:
    void fun(std::vector<int> &vi)
    {
        std::cout << sumVector(vi) << std::endl;
    }
Apart from defining overloaded versions, the overloaded versions can of course also be declared. E.g.,
    template <typename T>
    T sumVector(std::vector<T> &array);
Using templates may result in ambiguities which overloading cannot solve. Consider the following template function definition:
    template<typename T>
    bool differentSigns(T v1, T v2)
    {
        return
        (
            v1 < 0 && v2 >= 0
            ||
            v1 >= 0 && v2 < 0
        );
    }
Passing differentSigns() an int and an unsigned is an error, as the two types are different, whereas the template definition calls for identical types. Overloading doesn't really help here: defining a template having the following prototype is ok with the int and unsigned, but now two instantiations are possible with identical types.
    template<typename T1, typename T2>
    bool differentSigns(T1 v1, T2 v2);
This situation can be disambiguated by using template explicit arguments, e.g., differentSigns<int, int>(12, 30). But template explicit arguments could be used anyway with the second overloaded version of the function: in that case the first definition is superfluous and could have been omitted.

On the other hand, if one overloaded version can be interpreted as a more specialized variant of another version of a template function, then in principle the two variants of the template function could be used if the arguments are of the more specialized types. In this case, there is no ambiguity, as the compiler will use the more specialized variant if the arguments so suggest.

So, assume an overloaded version of sumVector() is defined having the following prototype and a snippet of code requiring the instantiation of sumVector:

    template <typename T>
    T sumVector(T, unsigned);

    extern int iArray[];

    void fun()
    {
        sumVector(iArray, 12);
    }
The above example doesn't produce an ambiguity, even though the original sumVector() given in section 18.1.1 and the version declared here could both be used for the call. Why is there no ambiguity here?

In situations like this there is no ambiguity if both declarations are identical but for the fact that one version is able to accept a superset of the possible arguments that are acceptable for the other version. The original sumVector() template can accept only a pointer type as its first argument. The version declared here can accept a pointer type as well as any non-pointer type. A pointer type iArray is passed, so both template functions are candidates for instantiation. However, the original sumVector() template function can only accept a pointer type as its first argument. It is therefore more specialized than the one given here, and it is therefore selected by the compiler. If, for some reason, this is not appropriate, then an explicit template argument can be used to overrule the selection made by the compiler. E.g.,

sumVector<int *>(iArray, 12);

18.1.7: Selecting an overloaded (template) function

The following steps determine the actual function that is called, given a set of (template or non-template) overloaded functions: If the template would have been declared as
    template <typename T>
    bool differentSigns(T t, T u);
then no template function would have been instantiated here. This is ok, as the ordinary function differentSigns(double, double) will now be used. An error occurs only if no instantiation of the template function can be generated and if no acceptable ordinary function is available. If such a case, the compiler will generate an error like
no matching function for call to `differentSigns (int &, double &)

As we've seen, a template function in which all type parameters exactly match the types of the arguments prevails over an ordinary function in which a (standard) type conversion is required. Correspondingly, a template explicitly specialized function will prevail over an instantiation of the general template if both instantiations show an exact match between the types of the parameters and the arguments. For example, if the following template declarations are available:

    template <typename T, typename U>
    bool differentSigns(T t, U u);

    template <> bool differentSigns<double, int>(double, int);
then the template explicitly specialized function will be selected without generating an extra instantiation from the general template definition.

Another situation in which an apparent ambiguity arises is when both an ordinary function is available and a proper instantiation of a template can be generated, both exactly matching the types of the arguments of the called function. In this case the compiler does not flag an ambiguity as the oridinary function is considered the more specialized function, which is therefore selected.

As a rule of thumb consider that when there are multiple viable functions sharing the top ranks of the set of viable functions, then the function template instantiations are removed from the set. If only one function remains, it is selected. Otherwise, the call is ambiguous.

18.1.8: Name resolution within template functions

Consider once more our function sumVector() of section 18.1.1, but now it's given a somewhat different implementation:
    template <typename T>
    T sumVector(T *array, unsigned n)
    {
        T
            sum = accumulate(array, array + n, T());        

        cout << "The array has " << n << " elements." << endl;
        cout << "The sum is " << sum << endl;

        return sum;
    }        
In this template definition, cout's operator<<() is called to display, a char const * text, an unsigned, and a T-value. The first cout statement displays the text and the unsigned value, no matter what happens in the template. These types do not depend on a template parameter. If a type does not depend on a template parameter, the necessary declarations for compiling the statement must be available when the definition of the template is given. In the above template definition this implies that
    std::ostream &std::ostream::operator<<(unsigned)
and
    std::ostream &std::ostream::operator<<(char const *)
must be known to the compiler when it reads the definition of the template. On the other hand,
    std::cout << ... << sum << std::endl
cannot be compiled by the time the template's definition is given, as the type of the variable sum depends on a template parameter. The statement can therefore only be checked for semantical correctness (i.e., the question whether sum can actually be inserted into cout) using operator<<() at the point where the template function is instantiated.

Names (variables) whose type depend on a template parameter are resolved when the template is instantiated: at that point the relevant declarations must be available. The location where this happens is called the template's point of instantiation. As a rule of thumb, make sure that the necessary declarations (usually: header files) are available at every point of instantiation of the template.

18.2: Template classes

Like templates for functions, templates can be constructed for complete classes. The construction of a template class can be considered when the class should be available for different types of data. Template classes are frequently used in C++: chapter 12 covers general data structures like vector, stack and queue, which are available as template classes. With template classes, the algorithms and the data on which the algorithms operate are completely separated from each other. To use a particular data structure on a particular data type, only the data type needs to be specified at the definition or declaration of the template class object, e.g., stack<int> istack.

In the upcoming sections the construction of template classes is discussed. In a sense, template classes compete with object oriented programming (cf. chapter 14), where a similar mechanism is seen. Polymorphism allows the programmer to separate algorithms from data, by deriving classes from the base class in which the algorithm is implemented, while implementing the data in the derived class, together with member functions that were defined as pure virtual functions in the base class to handle the data.

Generally, template classes are easier to use. It is certainly easier to write stack<int> istack to create a stack of ints than it is to derive a new class Istack: public stack and to implement all necessary member functions to be able to create a similar stack of ints using object oriented programming. On the other hand, for each different type that is used with a template class the complete class is reinstantiated, whereas in the context of object oriented programming the derived classes use, rather than copy, the functions that are already available in the base class.

Below a simple version of the template class Vector is constructed: the essential characteristics of a template class are illustrated, without attempting to redo the existing vector class completely.

18.2.1: Template class definitions

The construction and use of template classes will be covered in the next sections, in which a simple template class Vector will be constructed.

The construction of a template class can normally begin with the construction of a normal class interface around a hypothetical type Type. If more hypothetical types are required, then hypothetical types U, V, W, etc. can be used as well. Assume we want to construct a class Vector, that can be used to store values of type Type. We want to provide the class with the following members:

Should the set of members include members that can be used with const objects? In practical situations it probably should, but for now these members are not included in the interface: We've left them for the reader to implement.

Now that we have decided which members we want, the class interface can be constructed. Like template functions, a template class definition begins with the keyword template, which is followed by a non-empty list of template type and/or non-type parameters, surrounded by angular brackets. This template announcement is followed by the class interface, in which the template parameters may be used to represent types and constants. Here is the first form of the class interface of the Vector template class, already containing the private member function construct , which is used in the implementation of the copy constructor, the destructor, and the overloaded assignment operator. The class also already contains an iterator type: it's simply defined as a pointer to an element of the vector. The reverse_iterator will be added later. Note that the Vector template class contains only one template type parameter, and no non-type parameter.

    template <typename Type>
    class Vector
    {
        void construct(Vector<Type> const &other);
        Type 
            *d_start,
            *d_finish,
            *d_end_of_storage;
        public:
            typedef reverse_iter<Type> reverse_iterator;

            Vector();
            Vector(unsigned n);
            Vector(Vector<Type> const &other);
            ~Vector();
            Vector<Type> const &operator=(Vector<Type> const &other);
            Type &operator[](int index);
            Vector<Type> &sort();
            void push_back(Type const &value);
            Type *begin();
            Type *end();
            reverse_iterator rbegin();
            reverse_iterator rend();
            unsigned size();
    };
Within the class interface definition the abstract type Type can be used as a normal typename. However, note the Vector<Type> constructions appearing in the interface: there is no plain Vector, as the Vector will be bound to a type Type, to be specified later on in the program using a Vector.

Different from template functions, template class parameters can have default arguments. This holds true both for template type- and template non-type parameters. If a template class is instantiated without specifying arguments for the template parameters, and if default template parameters were defined, then the defaults are used. Such defaults should be suitable for a majority of instantiations of the class. E.g., for the template class Vector the template announcement could have been altered to specify int as the default type:

template <typename Type = int>

The class contains three data members: pointers to the begin and end of the allocated storage area (respectively data and end_of_storage) and a pointer pointing just beyond the element that was last allocated. The allocation scheme will add extra elements beyond the ones that are actually required to reduce the number of times the vector must be reallocated to accomodate new elements.

Template classes may be declared by removing the template interface definition (the part between the curly braces), replacing the definition by a semicolon:

    template <typename Type>
    class Vector;
here too, default types may be specified.

18.2.2: Template class instantiations

Template classes are instantiated when an object of a template class is defined. When a template class object is defined (or declared) the template parameters must explicitly be specified (note that the parameters having default arguments are also specified, albeit as defaults). Template arguments are never deducted, as with template functions. To define a Vector to store ints, the construction
    Vector<int>
        vInt;
is used. For a Vector for strings
    Vector<std::string>
        vString;
is used.

In combination with the keyword extern template class objects can be declared rather than defined. E.g.,

    extern Vector<int>
        vInt;
Template (type) parameters can be used to specify a template type of a template that is used within a template (for example, when a template class uses composition involving an object of another template class). In the following example a template function cloneVector() is defined, using type parameter T. It receives, defines, and returns Vector references and objects:
    template <typename T>
    Vector<T> cloneVector(Vector<T> &vector)
    {
        return Vector<T>(vector);
    }
A template class is not instantiated if a reference or pointer to the class template is used. In the above example, the return Vector<int>(vector) statement results in a template instantiation, but the parameter of the function, being a reference, won't result in a template instantiation. However, if a member function of a template class is used with a pointer or reference to a template class object, then the class is instantiated. E.g., in the following code
    #include "vector.h"

    void add(Vector<int> &vector, int value)
    {
        vector.push_back(value);
    }
the class Vector<int> will be instantiated.

18.2.3: Non-type parameters

Template nontype parameters must be constant expressions. I.e., the compiler must be able to evaluate their values. For example, the following class uses a template type parameter to define the type of the elements of a buffer, and a template nontype parameter to define the size of the buffer:
    template <typename Type, unsigned size>
    class Buffer
    {
        public:
            Type
                buffer[size];
    };
The size parameter must be a constant value when a Buffer object is defined or declared. E.g.,
    Buffer<int, 20>
        buffer;
Note that

18.2.4: Template class member functions

Normal design considerations should be followed when constructing template class member functions or template class constructors: template class type parameters should preferably be defined as T const &, rather than T, to prevent unnecessary copying of large data structures. Template class constructors should use member initializers rather than member assignment within the body of the constructors, again to prevent double assignment of composed objects: once by the default constructor of the object, once by the assignment itself.

Template member functions must be known to the compiler when the template is instantiated. The current Gnu g++ compiler does not support precompiled template classes, therefore the member functions of templates are inline functions. They can be defined inside the template interface or outside the template interface. Inline template member functions are defined in the same way as inline member functions of non-templates classes. However, for the member functions that are defined outside of the template's interface

In the Vector template class a member function
Type &operator[](unsigned index) throw(char const *)

could be declared. When defined outside of the template's interface, its implementation would be:

    template <typename T>
    T &Vector<T>::operator[](unsigned index) throw(char const *)
    {
        if (index >= (beyond - data))
            throw "Vector array index out of bounds";
        return data[index];
    }
Note the fact that the class type of operator[]() is the generic Vector<T> type. The abstract data type T is used to define the type of the return value of the member function.

18.2.5: Template classes and friend declarations

Template classes may declare other functions and classes as friends. There are three types of friend declarations that can appear within a template class: In the following sections the three types of friend declarations will be discussed in some detail.

18.2.5.1: Non-template friends

A template class may declare another function or class or class member function as its friend. Such a friend may access the private members of the template class. Classes and ordinary functions can be declared as friend, but a class interface must have been seen by the compiler before one of its members can be declared a friend of a template class (in order to verify the name of the friend function against the interface).

For example, here are some friend declarations:

    class Friend
    {
        public:
            void member();
    };

    template <typename T>
    class Vector
    {
        friend class AnotherFriend;     // declaration only is ok here
        friend void anotherMember();    // declaration is ok here
        friend Friend::member();        // Friend interface class required.
    };
Such ordinary friends can be used, e.g., to access static private members of a class or they can themselves define objects of the class declaring them as friends to access all members of these objects.

18.2.5.2: Bound friends

With bound friend template classes or functions there is a one-to-one mapping between the types that are used with the instantiations of the friends and the template class declaring them as friends. Here the friends are themselves templates. For example:

    template <typename T>
    class Friend;                   // declare a template class        

    template <typename T>
    void function(Friend<T> &t);    // declare a template function

    template <typename T>
    class AnotherFriend
    {
        public:
            void member();          // a member function within a class
    }

    template <typename T>
    class Vector
    {
        friend class Friend<T>;                     // 1 (see the text)
        friend void function<T>(Friend<T> t);       // 2
        friend void AnotherFriend<T>::member();     // 3
    };
Above, three friend declarations are illustrated: Assume we would like to be able to insert the elements of a Vector into an ostream object, using operator<<(). For such a situation the copy() generic algorithm in combination with the ostream_iterator<>() comes in handy. However, the latter iterator is a template function, depending on type T. If we can assume that data and beyond can be used as iterators of Vector, then the implementation is quickly realized by defining operator<<() as a bound template function, and by declaring this operator as a friend of the class Vector():
    #include <algorithm>
    #include <iostream>

    template<typename T>
    class Vector
    {
        friend std::ostream &operator<< <T> (std::ostream &str, 
                                            Vector<T> const &vector);
        T *d_data;
        T *d_beyond;
    };        

    template <typename T>
    std::ostream &operator<<(std::ostream &str, Vector<T> const &vector)
    {
        std::copy(vector.d_data, vector.d_beyond, 
                            std::ostream_iterator<T *>(str, " "));
        return str;
    }

18.2.5.3: Unbound friends

By prepending the friend keyword to the template<typelist> phrase, friends received their own template parameter list. The template types of these friends are completely independent from the type of the template class declaring the friends. Such friends are called unbound friends. Every instantiation of an unbound friend has unrestricted access to the private members of every instantiation of the template class declaring the friends.

Here is the syntactic convention for declaring an unbound friend function, an unbound friend class and an unbound friend member function of a class:

    template <typename Type>
    class Vector
    {
        template <typename T>
        friend void function(); // unbound friend function

        template <typename T>
        friend class Friend;    // unbound friend class

        template <typename T>      // unbound friend member function
        friend void AnotherFriend<T>::member();
    };

18.2.6: Template classes and static data

When static members are defined in a template class, these static members are instantiated for every different instantiation of the template class. As they are static members, there will be only one member when multiple objects of the same template type(s) are defined. For example, in a class like:
    template <typename Type>
    class TheClass
    {
        static int s_objectCounter;        
    };
There will be one TheClass<Type>::objectCounter for each different Type. However, the following will result in just one static variable, which is shared among the different objects:
    TheClass<int> theClassOne;
    TheClass<int> theClassTwo;
Remember that static members are only declared in their classes. They must be defined separately. With static members of template classes this is not different. But, comparable to the implementations of static functions, the definitions of static members are usually provided in the same file as the template class interface itself. The definition of the static member objectCounter is therefore:
    template <typename Type>
    class TheClass
    {
        static int s_objectCounter;         // declaration
                
    };

    template <typename Type>                // definition
    int TheClass<Type>::s_objectCounter = 0;
In the above case objectCounter is an int, and thus independent of the template type parameter Type. In a list-like construction, where a pointer to objects of the class itself is required, the template type parameter Type does enter the definition of the static variable, as is shown in the following example:
    template <typename Type>
    class TheClass
    {
        static TheClass *s_objectPtr;
    };

    template <typename Type>
    TheClass<Type> *TheClass<Type>::s_objectPtr = 0;
Note here that the definition can be read, as usual, from the variable name back to the beginning of the definition: objectPtr of the class TheClass<Type> is a pointer to an object of TheClass<Type>.

18.2.7: Derived Template Classes

Template classes can be used in class derivation as well. Consider the following base class:
    template<typename T>
    class Base
    {
        T const &t;

        public:
            Base(T const &t)
            :
                t(t)
            {}
    };
The above class is a template class, which can be used as a base class for the following derived template class Derived:
    template<typename T>
    class Derived: public Base<T>
    {
        public:
            Derived(T const &t)
            :
                Base(t)
            {}
    };
Other combinations are possible as well: By specifying concrete template type parameters of the base class, the base class is instantiated and the derived class becomes an ordinary (non-template) class:
    class Ordinary: public Base<int>
    {
        public:
            Ordinary(int x)
            :
                Base(x)
            {}
    };

    // With the following object definition:
    Ordinary
        o(5);
This construction allows us in a specific situation to add functionality to a template class, without the need for constructing a derived template class.

18.2.8: Nesting and template classes

When a class is nested within a template class, it automatically becomes a template class itself. The nested class may use the template parameters of the surrounding class, as shown in the following skeleton program. Within the class Vector the class reverse_iterator is defined. The class receives its information from the surrounding class, a Vector<Type> class. Since this surrounding class is the only class which should be able to construct its reverse iterators, the constructor of reverse_iterator is made private, and the surrounding class is given access to the private members of reverse_iterator using a bound friend declaration.

Also, the surrounding class is allowing access to its private members by the class reverse_iterator, so once the definition of the class reverse_iterator has been seen by the compiler, we're ready to declare reverse_iterator a friend of Vector<Type>.

Once the classes are set up, a main program constructs a Vector<int>, which is used to construct a Vector<int>::reverse_iterator, using its rbegin() member. This member, however, returns an anonymous (and thus: constant) reverse_iterator object. So the public copy constructor is used to make a non-constant reverse_iterator object available in main(), as the rbegin object. Here is the skeleton program, omitting all data members and extra member functions:

    #include <iostream>

    template <typename Type>
    class Vector
    {
        class reverse_iterator
        {
            friend class Vector<Type>;

            reverse_iterator(Vector<Type> *vector);

            public:
                reverse_iterator(reverse_iterator const &other);
                Type &operator*() const;
        };

        friend class reverse_iterator;

        public:
            Vector();
            reverse_iterator rbegin()
            {
                return reverse_iterator(this);
            }
    };
            
    int main()
    {
        Vector<int>
            vi;

        Vector<int>::reverse_iterator
            rbegin = vi.rbegin();

        std::cout << *rbegin;
    }

Nested enumerations and typedefs can be defined in template classes as well. For example, with arrays the distinction between the last index that can be used and the number of elements frequently causes confusion in people who are exposed to C-array types for the first time. The following construction automatically provides valid last and nElements definition:

    template<typename Type, int size>
    class Buffer
    {
        Type    *b;

        public:
            enum Limits
            {
                last = size - 1,
                nElements
            };
            typedef Type elementType;

            Buffer()
            :
                b(new Type [size])
            {}
    };
This small example defines Buffer<Type, size>::elementType, Buffer<Type, size>::last and Buffer<Type, size>::nElements (as values), as well as Buffer<Type, size>::Limits and Buffer<Type, size>::elementType> (as typenames).

Of course, the above represents the template form of these values and declarations. They must be instantiated before they can be used. E.g,

Buffer<int, 80>::elementType

is a synonym of int.

Note that a construction like Buffer::elementType is illegal, as the type of the Buffer class remains unknown.

18.2.9: Member templates

Template classes or functions can also be defined within other classes (which itself may or may not be a template class). Such a nested template function or class is called a member template. It is defined the same way as any other ordinary template class, including the template <typename ...> header. E.g.,
    template <typename Type1>
    class Outer
    {
        public:
            template <typename Type2>              // template class
            class Inner
            {
                public:
                    Type1
                        variable1;
                    Type2
                        variable2;
            };
                        
            template <typename Type3>              // template function
            Type3 process(Type3 const &p1, Type3 const &p2)
            {
                Type3
                    result;

                return result;
            }
    };
The special characteristic of a member template is that it can use its own and its surrounding class' template parameters, as illustrated by the definition of variable1 in the class Inner. Here are some consequences of using templates in classes

18.2.10: Template class specializations

Template class specializations are used in cases where template member functions cannot be used with a (class) type for which the template is instantiated. In those cases template member functions can be explicitly constructed to suit the needs of the particular type for which the template is instantiated. These explicitly constructed members are called template class specializations.

Assume we have a template class which supports the insertion of its type parameter into an ostream. E.g.,

    template <typename Type>
    class Inserter
    {
        public:
            Inserter(Type const &t)
            :
                object(t)
            {}
            ostream &insert(ostream &os) const
            {
                return os << object;
            }
        private:
            Type 
                object;
    };
In the example a plain member function is used to insert the current object into an ostream. The implementation of the insert() function shows that it uses the operator<<, as defined for the type that was used when the template class was instantiated. E.g., the following little program instantiates the class Inserter<int>:
    int main()
    {
        Inserter<int>
            ins(5);
    
        ins.insert(cout) << endl;
    }
Now suppose we have a class Person having the following member function:
    class Person
    {
        public:
            std::ostream &insert(std::ostream &ostr) const;
    };
This class cannot be used to instantiate Inserter, as it does not have a operator<<() function, which is used by the function Inserter<Type>::insert(). Attempts to instantiate Inserter<Person> will result in a compilation error. For example, consider the following main() function:
    int main()
    {
        Person 
            person;
        Inserter<Person>
            p2(person);
    
        p2.insert(std::cout) << endl;
    }
If this function is compiled, the compiler will complain about the missing function std::ostream & << const Person &, which was indeed not available. However, the function std::ostream &Person::insert(std::ostream &ostr) is available, and it serves the same purpose as the required function std::ostream & Inserter<Person>::insert(std::ostream &).

For this situation multiple solutions exist. One would be to define an operator<<(Person const &p) function which calls the Person::insert() function. But in the context of the Inserter class, this might not what we want. Instead, we might want to look for a solution that is closer to the class Inserter.

Such a solution exists in the form of a template class specialization. Such an explicit specialization definition starts with the word template, then two angular brackets (<>), which is then followed by the function definition for the nstantiation of the template class for the specific template parameters. So, with the above function this yields the following function definition:

    template<>
    std::ostream  &Inserter<Person>::insert(std::ostream &os) const
    {
        return object.insert(os);
    }
Here we explicitly define a function insert of the class Inserter<Person>, which calls the appropriate function that lives in the Person class.

Note that the explicit specialization definition is a true definition: it should not be given in the header file of the Inserter template class, but it should have its own source file. However, in order to inform the compiler that an explicit specialization is available, it can be declared in the template's header file. The declaration is straightforward: the code-block is replaced by the semicolon:

    template<>
    std::ostream  &Inserter<Person>::insert(std::ostream &os) const;
It is also possible to specialize a complete template class. The following shows such a specialization for the above class Inserter applied to the double primitive type:
    template <>
    class Inserter
    {
        public:
            Inserter<double>(double const &t);
            std::ostream &insert(std::ostream &os) const;
        private:
            double
                object;
    };
The explicit template class specialization is obtained by replacing all references to the template's class name Inserter by the class name and the type for which the specialization holds true: Inserter<double>, and by replacing occurrences of the template's type parameter by the actual type for which the specialization was constructed. The template class specialization interface must be provided after the interface of the original template class. The definition of its members are, analogously to the Inserter<Person>::insert() function above, given in separate source files. However, in the case of a complete template class specialization, the definitions of its members should not be preceded by the template<> prefix. E.g.,
    Inserter<double>(double const &t)   // NO template<> prefix !
    :
        object(t)
    {}

18.2.11: Template class partial specializations

In cases where a template has more than one parameter, a partial specialization rather than a full specialization might be appropriate. With a partial specialization, a subset of the parameters of the original template can be redefined.

Let's assume we are working on a image processing program. A class defining an image receives two int template parameters, e.g.,

    template <int columns, int rows>
    class Image        
    {
        public:
            Image()
            {
                // uses 'columns' and 'rows'
            }
    };
Now, assume that an image having 320 columns deserves special attention, as those pictures allow us to use, e.g., a special smoothing algorithm. From the general template given above we can now construct a partially specialized template, which only has a columns parameter. Such a template is like an ordinary template parameter, in which only the rows remain as a template parameter. When the specialized class is defined, the specialization is made explicit by mentioning a specialization parameter list:
    template <int rows>
    class Image<320, rows>
    {
        public:
            Image()
            {
                // use 320 columns and 'rows' rows.
            }
    };
With the above partially specialized template definition the 320 columns are explicitly mentioned at the class interface, while the rows remain variable. Now, if an image is defined as
    Image<320, 240>
        image;
two instantiations could be used: the fully general template is a candidate as well as the partially specialized template. Since the partially specialized template is more specialized than the fully general template, the Image<320, rows> template will be used. This is a general rule: a more specialized template instantiation is chosen in favor of a more general one whereever possible.

Every template parameter can be used for the specialization. In the last example the columns were specialized, but the rows could have been specialized as well. The following partial specialization of the template class Image specializes the rows parameter and leaves the columns open for later specification:

    template <int columns>
    class Image<columns, 200>
    {
        public:
            Image()
            {
                // use 'columns' columns and 200 rows.
            }
    };
Even when both specializations are provided there will (generally) be no problem. The following three images will result in, respectively, an instantiation of the general template, of the template that has been specialized for 320 columns, and of the template that has been specialized for the 200 rows:
    Image<1024, 768>
        generic;
    Image<320, 240>
        columnSpecialization;
    Image<480, 200>
        rowSpecialization;
With the generic image, no specialized template is available, so the general template is used. With the columnSpecialization image, 320 columns were specified. For that number of columns a specialized template is available, so it's used. With the rowSpecialization image, 200 rows were specified. For that number of rows a specialized template is available, so that specialized template is used with rowSpecialization.

One might wonder what happens if we want to construct the following image:

    Image<320, 200>
        superSpecialized;
Is this a specialization of the columns or of the rows? The answer is: neither. It's ambiguous, precisely because both the columns and the rows could be used with a (differently) specialized template. If such an image is required, yet another specialized template is needed, albeit that that one is not a partially specialized template anymore. Instead, it specializes all its parameters with the class interface:
    template <>
    class Image<320, 200>
    {
        public:
            Image()
            {
                // use 320 columns and 200 rows.
            }
    };
The above super specialization of the Image template will be used with the image having 320 columns and 200 rows.

18.2.12: Name resolution within template classes

In section 18.1.8 the name resolution process with template functions was discussed. As is the case with template functions, name resolution in template classes also proceeds in two steps. Names that do not depend on template parameters are resolved when the template is defined. E.g., if a member function in a template class uses a qsort() function, then qsort() does not depend on a template parameter. Consequently, qsort() must be known when the compiler sees the template definition, e.g., by including the file cstdlib or stdlib.h.

On the other hand, if a template defines a <typename Type> template parameter, which is the returntype of some template function, e.g.,

    Type returnValue();
then we have a different situation. At the point where template objects are defined or declared; at the point where template member functions are used; and at the point where static data members of template classes are defined or declared, it must be able to resolve the template type parameters. So, if the following template class is defined:
    template <typename Type>
    class Resolver
    {
        Type        d_datum;
        static int  d_value;

        public:
            Resolver();
            Type result();
    };
Then string must be known before each of the following examples can be compiled:
    // ----------------------- example 1: define the static variable
    int Resolver<std::string>::d_value = 12;   

    // ----------------------- example 2: define a Resolver object
    int main()
    {
        Resolver<std::string>   resolver;
    }

    // ----------------------- example 3: declare a Resolver object
    extern Resolver<string>
        resolver;

18.3: Constructing iterators

In section 17.2 the iterators used with generic algorithms were introduced. We've seen that several types of iterators are distinguished by the generic algoritms: InputIterators, ForwardIterators, OutputIterators, BidirectionalIterators en RandomAccessIterators.

Also, in section 17.2 the characterstics of iterators were introduced: basically it should be possible to increment iterators, to dereference iterators and to compare iterators for equality.

However, in order to use iterators in the context of the generic algorithms iterators often must meet extra requirements. This is caused by the fact that generic algorithms check the types of the iterators they receive. Simple pointers are usually accepted, but if a class is used as an iterator, the iterator-class must be able to specify the kind of iterator it represents.

To make sure that an object of a class is interpreted as a particular type of iterator, the class must be derived from the classes input_iterator, output_iterator, forward_iterator, bidirectional_iterator, or random_access_iterator. These classes mainly define tags which are used by generic algorithms to check the correct types of the provided iterators, and have nothing to do with the actual implementation of the iterators. In order to derive classes from the iterator-classes, sources must

    #include <iterator>

To construct a class Iterator that can be used as an iterator (which could be an ordinary iterator, a const_iterator, a reverse_iterator or a const_reverse_iterator) for type `Type' (which may be a template type parameter or a specific type like an int or a string), one must be prepared to implement several members. Also, these iterator-classes must be derived from appropriate iterator-tag classes. With the exception of the output_iterator base class, these iterator classes are template classes themselves, using Type and ptrdiff_t as their template class parameters. The following iterator classes can thus be constructed:

Section 19.6 shows the implementation of the most complex of these iterators, the RandomAccessIterator, in the context of the Vector class.