|C H A P T E R 6|
Creating and Using Templates
Templates make it possible for you to write a single body of code that applies to a wide range of types in a type-safe manner. This chapter introduces template concepts and terminology in the context of function templates, discusses the more complicated (and more powerful) class templates, and describes the composition of templates. Also discussed are template instantiation, default template parameters, and template specialization. The chapter concludes with a discussion of potential problem areas for templates.
A function template describes a set of related functions that differ only by the types of their arguments or return values.
You must declare a template before you can use it. A declaration, as in the following example, provides enough information to use the template, but not enough information to implement the template.
In this example, Number is a template parameter; it specifies the range of functions that the template describes. More specifically, Number is a template type parameter, and its use within the template definition stands for a type determined at the location where the template is used.
If you declare a template, you must also define it. A definition provides enough information to implement the template. The following example defines the template declared in the previous example.
Because template definitions often appear in header files, a template definition might be repeated in several compilation units. All definitions, however, must be the same. This restriction is called the One-Definition Rule.
The compiler does not support expressions involving non-type template parameters in the function parameter list, as shown in the following example.
Once declared, templates can be used like any other function. Their use consists of naming the template and providing function arguments. The compiler can infer the template type arguments from the function argument types. For example, you can use the previously declared template as follows.
If a template argument cannot be inferred from the function argument types, it must be supplied where the function is called. For example:
A class template describes a set of related classes or data types that differ only by types, by integral values, by pointers or references to variables with global linkage, or by a combination thereof. Class templates are particularly useful in describing generic, but type-safe, data structures.
A class template declaration provides only the name of the class and its template arguments. Such a declaration is an incomplete class template.
The following example is a template declaration for a class named Array that takes any type as an argument.
This template is for a class named String that takes an unsigned int as an argument.
A class template definition must declare the class data and function members, as in the following examples.
Unlike function templates, class templates can have both type parameters (such as class Elem) and expression parameters (such as unsigned Size). An expression parameter can be:
The full definition of a class template requires definitions for its function members and static data members. Dynamic (nonstatic) data members are sufficiently defined by the class template declaration.
The definition of a template function member consists of the template parameter specification followed by a function definition. The function identifier is qualified by the class template's class name and the template arguments. The following example shows definitions of two function members of the Array class template, which has a template parameter specification of template <class Elem>. Each function identifier is qualified by the template class name and the template argument Array<Elem>.
This example shows definitions of function members of the String class template.
The definition of a template static data member consists of the template parameter specification followed by a variable definition, where the variable identifier is qualified by the class template name and its template actual arguments.
A template class can be used wherever a type can be used. Specifying a template class consists of providing the values for the template name and arguments. The declaration in the following example creates the variable int_array based upon the Array template. The variable's class declaration and its set of methods are just like those in the Array template except that Elem is replaced with int (see Section 6.3, Template Instantiation).
The declaration in this example creates the short_string variable using the String template.
You can use template class member functions as you would any other member function.
Template instantiation involves generating a concrete class or function (instance) for a particular combination of template arguments. For example, the compiler generates a class for Array<int> and a different class for Array<double>. The new classes are defined by substituting the template arguments for the template parameters in the definition of the template class. In the Array<int> example, shown in the preceding "Class Templates" section, the compiler substitutes int wherever Elem appears.
The use of a template function or template class introduces the need for an instance. If that instance does not already exist, the compiler implicitly instantiates the template for that combination of template arguments.
The compiler implicitly instantiates templates only for those combinations of template arguments that are actually used. This approach may be inappropriate for the construction of libraries that provide templates. C++ provides a facility to explicitly instantiate templates, as seen in the following examples.
To instantiate a template function explicitly, follow the template keyword by a declaration (not definition) for the function, with the function identifier followed by the template arguments.
Template arguments may be omitted when the compiler can infer them.
To instantiate a template class explicitly, follow the template keyword by a declaration (not definition) for the class, with the class identifier followed by the template arguments.
When you explicitly instantiate a class, all of its members are also instantiated.
To explicitly instantiate a template class function member, follow the template keyword by a declaration (not definition) for the function, with the function identifier qualified by the template class, followed by the template arguments.
To explicitly instantiate a template class static data member, follow the template keyword by a declaration (not definition) for the member, with the member identifier qualified by the template class, followed by the template argument.
You can use templates in a nested manner. This is particularly useful when defining generic functions over generic data structures, as in the standard C++ library. For example, a template sort function may be declared over a template array class:
and defined as:
The preceding example defines a sort function over the predeclared Array class template objects. The next example shows the actual use of the sort function.
You can give default values to template parameters for class templates (but not function templates).
If a template parameter has a default value, all parameters after it must also have default values. A template parameter can have only one default value.
There may be performance advantages to treating some combinations of template arguments as a special case, as in the following examples for twice. Alternatively, a template description might fail to work for a set of its possible arguments, as in the following examples for sort. Template specialization allows you to define alternative implementations for a given combination of actual template arguments. The template specialization overrides the default instantiation.
You must declare a specialization before any use of that combination of template arguments. The following examples declare specialized implementations of twice and sort.
You can omit the template arguments if the compiler can unambiguously determine them. For example:
You must define all template specializations that you declare. The following examples define the functions declared in the preceding section.
A specialization is used and instantiated just as any other template, except that the definition of a completely specialized template is also an instantiation.
In the previous examples, the templates are fully specialized. That is, they define an implementation for specific template arguments. A template can also be partially specialized, meaning that only some of the template parameters are specified, or that one or more parameters are limited to certain categories of type. The resulting partial specialization is itself still a template. For example, the following code sample shows a primary template and a full specialization of that template.
The following code shows examples of partial specialization of the primary template.
This section describes problems you might encounter when using templates.
Sometimes a template definition uses names that are not defined by the template arguments or within the template itself. If so, the compiler resolves the name from the scope enclosing the template, which could be the context at the point of definition, or at the point of instantiation. A name can have different meanings in different places, yielding different resolutions.
Name resolution is complex. Consequently, you should not rely on nonlocal names, except those provided in a pervasive global environment. That is, use only nonlocal names that are declared and defined the same way everywhere. In the following example, the template function converter uses the nonlocal names intermediary and temporary. These names have different definitions in use1.cc and use2.cc, and will probably yield different results under different compilers. For templates to work reliably, all nonlocal names (intermediary and temporary in this case) must have the same definition everywhere.
A common use of nonlocal names is the use of the cin and cout streams within a template. Few programmers really want to pass the stream as a template parameter, so they refer to a global variable. However, cin and cout must have the same definition everywhere.
The template instantiation system relies on type-name equivalence to determine which templates need to be instantiated or reinstantiated. Thus local types can cause serious problems when used as template arguments. Beware of creating similar problems in your code. For example:
The Foo type as registered in file1.cc is not the same as the Foo type registered in file2.cc. Using local types in this way could lead to errors and unexpected results.
Templates must be declared before they are used. A friend declaration constitutes a use of the template, not a declaration of the template. A true template declaration must precede the friend declaration. For example, when the compilation system attempts to link the produced object file for the following example, it generates an undefined error for the operator<< function, which is not instantiated.
Note that there is no error message during compilation because the compiler reads the following as the declaration of a normal function that is a friend of the array class.
Because operator<< is really a template function, you need to supply a template declaration for prior to the declaration of template class array. However, because operator<< has a parameter of type array<T>, you must precede the function declaration with a declaration of array<T>. The file array.h must look like this:
The C++ standard requires types with qualified names that depend upon template arguments to be explicitly noted as type names with the typename keyword. This is true even if the compiler can "know" that it should be a type. The comments in the following example show the types with qualified names that require the typename keyword.
Because the ">>" character sequence is interpreted as the right-shift operator, you must be careful when you use one template names inside another. Make sure you separate adjacent ">" characters with at least one blank space.
For example, the following ill-formed statement:
is interpreted as:
The correct syntax is:
Within a template definition, the compiler does not support referencing an object or function that is declared static at global scope or in a namespace. If multiple instances are generated, the One-Definition Rule (C++ standard section 3.2) is violated, because each instance refers to a different object. The usual failure indication is missing symbols at link time.
If you want a single object to be shared by all template instantiations, then make the object a nonstatic member of a named namespace. If you want a different object for each instantiation of a template class, then make the object a static member of the template class. If you want a different object for each instantiation of a template function, then make the object local to the function.
If you are building more than one program or library by specifying -instances=extern, it's advisable to build them in separate directories. If you want to build in the same directory then you should clean the repository between the different builds. This avoids any unpredictable errors. For more information see Section 7.4.4, Sharing Template Repositories.
Consider the following example with make files a.cc, b.cc, x.h, and x.cc. Note that this example is meaningful only if you specify -instances=extern:
If you build both a and b, add a make clean between the two builds. The following commands result in an error:
The following commands will not produce any error: