Templates |
3 |
![]() |
The purpose of templates is to enable programmers to write a single body of code that applies to a wide range of types in a type-safe manner. Before reading this chapter, you should be familiar with the discussion of templates in C++ Annotated Reference Manual.
The C++ compiler's implementation of templates is based on the C++ Annotated Reference Manual with elements from the emerging ISO C++ standard.
The C++ compiler's implementation of templates is different from that of AT&T's Cfront compiler. See "Compile-Time Versus Link-Time Instantiation" on page 84. For additional information, refer to Appendix A, "Migration Guide".
Function Templates
A function template describes a set of related functions. That set is parameterized by types. Template Declaration
Templates must be declared before they can be used. A declaration, as in the following example, provides enough information to use the template, but not enough information to implement the template.
template <class Number> Number twice( Number original ); |
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 template declarations and definitions stands for some to-be-determined type.
Template Definition
If a template is declared, it must also be defined. A definition provides enough information to implement the template. The following example defines the template declared in the previous example.
template <class Number> Number twice( Number original ) { return original + original; } |
Because template definitions often appear in header files, a template definition may be repeated several times. All definitions, however, must be the same. This restriction is called the One-Definition Rule.
Template Use
Once declared, templates may be used like any other function. Their use consists of naming the template and providing template arguments corresponding to the template's parameters. For example, you may use the previously declared template as follows, explicitly specifying the template argument.
double example1( double item ) { return twice<double>( item ); } |
double example2( double item ) { return twice( item ); } |
If the compiler is unable to uniquely infer template arguments from the function arguments, it will issue an error. To correct the problem, simply specify the template arguments.
Template Instantiation
The process of instantiation involves generating a concrete function (instance) for a particular combination of template arguments. For example, the compiler will generate a function for twice<int> and a different function for twice<double>. Instantiation may be implicit or explicit. Implicit Instantiation
The use of a template function introduces the need for an instance. If that instance does not already exist, the compiler will implicitly instantiate the template for that combination of template arguments. Explicit Instantiation
The compiler will implicitly instantiate 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 in the following example:
template float twice<float>( float original ); |
Template parameters and arguments may be omitted when the compiler can infer them, as in:
template int twice( int original ); |
Class Templates
A class template describes a set of related classes, or data types. That set is parameterized by types, by integral values, and/or by some variable references. Class templates are particularly useful in describing generic, but type-safe, data structures. Template Declaration
A class template declaration must declare the class data and function members, as in the examples below. In these examples, template arguments are Size, an unsigned int, and Elem, a type.
template <class Elem> class Array { Elem* data; int size; public: Array( int sz ); int GetSize( ); }; |
template <unsigned Size> class String { char data[Size]; static int overflow; public: String( char *initial ); int length( ); }; |
Template Definition
The definition of a class template consists of a template definition for its member functions, and for its static data members. Dynamic (non-static) data members are sufficiently defined by the template declaration. Function Members
template <class Elem> Array<Elem>::Array( int sz )
{ size = sz; data = new Elem[ size ]; }
template <class Elem> int Array<Elem>::GetSize( )
{ return size; }
Static Data Members
Static data member definitions must always provide an initial value, otherwise, the statement is interpreted as a declaration.
template <unsigned Size> int String<Size>::overflow = 0;
Template Use
A template class may be used wherever a type may be used. Specifying a template class consists of providing the template name and the template arguments corresponding to the template parameters. The following examples define two variables, one of array and one string.
Array<int> int_array( 100 ); |
String<8> short_string( "hello" ); |
You may use template class member functions as you would any other member function. For example:
int x = int_array.GetSize( ); |
int x = short_string.length( ); |
Instantiation
The process of instantiation involves generating a concrete class (instance) for a particular combination of template arguments. For example, the compiler will generate 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 "Template Declaration", "Template Definition", and "Template Use" sections, the compiler substitutes int wherever Elem appears. Implicit Instantiation
The use of a template class introduces the need for an instance. If that instance does not already exist, the compiler will generate it. Explicit Instantiation
Implicit instantiation may be inappropriate when constructing libraries that provide templates. Templates may be explicitly instantiated, as in:
template class Array<char>; |
template class String<19>; |
When the programmer explicitly instantiates a class, all of its member functions are also instantiated.
Whole Class Instantiation
When the compiler implicitly instantiates a template class, it instantiates the static data members, the constructor, and the destructor. However, the compiler does not implicitly instantiate any other member function unless the function is explicitly referenced. To force the compiler to instantiate all member functions when implicitly instantiating a class, use the
-template=wholeclass compiler option. To turn this option off, specify
-template=no%wholeclass,which is the default.
Nested Template Use
Templates may be used (though not defined) in a nested manner. This is particularly useful in defining generic functions over generic data structures, as is done in the emerging Standard Template Library. For example, a template sort function may be declared over a template array class:
template <class Elem> void sort( Array<Elem> ); |
Array<int> int_array( 100 ); // construct our array of ints sort<int>( int_array ); // sort it |
Template Specialization, Standard Method
There may be significant performance advantages to treating some combinations of template arguments as a special case, as in the examples below for twice. Alternatively, a template description may fail to work for a set of its possible arguments, as in the examples below for sort. Template specialization enables the definition of alternate implementations for a given combination of actual template arguments. The template specialization overrides the default instantiation. Declaration
A specialization must be declared before any use of that combination of template arguments. The following examples declare specialized implementations of twice and sort.
template <> unsigned twice<unsigned>( unsigned original ); |
template <> sort<char*>( Array<char*> store ); |
The template arguments may be omitted if the compiler can unambiguously determine them. For example:
template <> unsigned twice( unsigned original ); |
template <> sort( Array<char*> store ); |
Definition
All template specializations declared must also be defined. The following examples define the functions declared above.
template <> unsigned twice<unsigned>( unsigned original ) { return original << 1; } |
Use and Instantiation
Specializations are used and instantiated just like any other template, except that the definition of a completely specialized template is also an instantiation.
Template File Organization
There are two primary organizations of template files, the definitions-included and the definitions-separated. The definitions-included organization enables more control over template compilation, and is recommended for that reason. Definitions-Included
When the declarations and definitions for a template are contained within the file that uses the template, the file organization is definitions-included. For example:
main.cc
|
template <class Number> Number twice( Number original ); template <class Number> Number twice( Number original ) { return original + original; } int main( ) { return twice<int>( -3 ); } |
It is very important to make template headers idempotent. That is, when a header is included many times, its effect is always the same. This is easiest to accomplish when the header turns itself off with #define and #ifndef, as in the preceding example.
Definitions-Separate
When the file that uses a template includes a file containing only the declaration of the template, and not the definition, the template file has the definitions-separate organization. The definition must appear in another file. For example:
Because of the separation of header and template source files, you must be very careful in file construction, placement, and naming. You may also need to explicitly identify the location of the source file to the compiler.
Potential Problem Areas
This section describes potential problem areas in using templates. Non-Local Name Resolution and Instantiation
Some names used within a template definition may not be 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. These alternatives may yield different resolutions. Name resolution is complex, and currently under debate in the C++ standards committee. Consequently, you should not rely on non-local names, except those provided in a pervasive global environment. In other words, a name may have different meanings in different places; use only non-local names that are declared everywhere and that mean the same thing everywhere. For example:
In this example, the template function converter uses the non-local names intermediary and temporary. These names have different definitions in the environment of each template, and will probably yield different results under different compilers. In order for templates to work reliably, all non-local names (intermediary and temporary) must have the same definition everywhere.
Local Types as Template Arguments
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.
Friend Declarations of Template Functions
The template instantiation system requires that a declaration follow the search rules in locating template definitions, that is, a declaration must precede an instantiation. This declaration must be a true declaration, and not a friend declaration. For example:
friend ostream& operator<<(ostream&, const array<T>&); |
template <class T> ostream& operator<<( ostream&, const array<T>& ); |
This declaration guarantees that the function is instantiated for each object of type array<T>.
Template Compilation
Template compilation is a complicated process because the compiler must instantiate templates at nearly arbitrary times during compilation, and because it may need to search for template definitions among several files. This section describes the major components of template compilation. Verbose Compilation
The C++ compiler will notify the users of significant events during template compilation when given the flag -verbose=template. Conversely, the compiler will not notify users when given -verbose=no%template, which is the default. The +w option may give other indications of potential problems when template instantiation occurs. Template Database
The template database is a directory containing all configuration files needed to handle and instantiate the templates required by your program. It also acts as a repository for all generated object files containing templates.
The template database is contained, by default, in the subdirectory Templates.DB within the current directory. You may specify the directory with the -ptrdirectory option. If the directory does not exist, and the compiler needs to instantiate a template, the directory is created for you.
Multiple Template Databases
You may specify multiple databases by giving multiple -ptr options. When specifying multiple databases, the first one to specify is your working, writable database. All others are read-only. To avoid confusion in multiple-database environments, make the first entry your current working directory. For example:
CC -ptr. -ptr/usr/lib/Templates.DB -o main main.cc |
Note - It is highly recommended that you do not rely on the use of the -ptr directive as its use may become obsolete in future releases. While alternatives will be available, the direct use of -ptr might cause compiler performance degradation in the future.
Using the Same Template Database for Multiple Targets
If you use the same template database for multiple targets, inconsistent results can occur. However, if you choose to build multiple targets in the same work directory, tdb_link notes this fact during parallel builds and implements a locking mechanism on the primary working database. If two or more links are attempted on the same database, tdb_link initiates a wait until the lock is cleared by the other compilation. The ptclean Command
Changes in your program can render some instantiations superfluous, thus wasting storage space. The ptclean command clears out the template database, removing all instantiations, temporary files, and dependency files. Instantiations will be recreated when, and only when, needed. Options File
The template options file, Template.opt, is a user-provided optional file that contains options needed to locate template definitions and to control instance recompilation. In addition, the options file provides features for controlling template specialization and explicit instantiation, although the user is discouraged from using them because the C++ compiler now supports the syntax required to declare specializations and explicit instantiation in the source code. Comment Entries
Comments start with a # character and extend to the end of the line. Text within a comment is ignored.
# Comment text is ignored until the end of the line. |
Sharing Options Files
You may share options files among several template databases by including the options files. This facility is particularly useful when building libraries containing templates. During processing, the specified options file is textually included in the current options file. You can have more than one include statement and place them anywhere in the options file. The options files can also be nested.
include "options-file"; |
Template Definition Location and Consistency Entries
The extensions and definition options file entries are described in "Template Definition Searching" on page 87.
Template Specialization Entries
The special options file entries are described in "Template Specialization, Deprecated Method" on page 92.
Compile-Time Versus Link-Time Instantiation
Instantiation is the process by which a C++ compiler creates a usable function or object from a template. Two common methods of template instantiation are compile-time instantiation and link-time instantiation. C++ Compile-Time Instantiation
C++ 4.2 uses compile-time instantiation, which forces instantiations to occur when the reference to the template is being compiled.
The two main disadvantages of link-time instantiation are:
Note - If you wish to create a library that contains all instances of the templates that it uses, specify the -xar option when creating the library. Do not use the ar command. For example: CC -xar libmain.a a.o b.o c.o
Template instances receive static linkage. These instances will not be visible or usable outside of the current compilation unit.
Specify static instance linkage with the -instances=static option.
Static instance linkage may only be used with the definitions-included template organization. The compiler will not search for definitions.
Templates may have identical instantations in several object files. This results in unnecessarily large programs. Static instance linkage is therefore suitable only for small programs, where templates are unlikely to be multiply instantiated.
Compilation is potentially faster with static instance linkage, so static linkage may also be suitable during fix-and-continue debugging.
Template instances receive global linkage. These instances will be visible and usable outside of the current compilation unit.
Specify global instance linkage with the -instances=global option.
Global instance linkage may only be used with the definitions-included template organization. The compiler will not search for definitions.
Templates may have identical instantations in several object files. This results in multiple definitions conflicts during linking. Global linkage is therefore suitable only when it is known that instances will not be repeated, such as when constructing libraries with explicit instantiation.
Instances are referenced from the current compilation unit with external linkage. Instances are defined within the template database with global linkage. The compiler ensures that exactly one template instance exists; instances will be neither undefined nor multiply defined.
Specify external linkage with the -instances=extern option. This option is the default.
Because instances are stored within the template database, you must use the CC command to link C++ objects that use external instance linkage into programs.
If you wish to create a library that contains all instances that it uses, specify the -xar option when creating the library. Do not use the ar command. For example:
CC -xar libmain.a a.o b.o c.o |
Template Definition Searching
When using the definitions-separate template file organization, template definitions are not available in the current compilation unit, and the compiler must search for the definition. This section describes the controls on that search. Definition searching is somewhat complex and prone to error. Therefore, you should use the definitions-included template file organization, which avoids definition searching altogether. Source File Location Conventions
Without specific directions as provided with an options file (see below), the compiler uses a Cfront-style method to locate template definition files. This method requires that the template definition file contain the same base name as the template declaration file, and that it also be on the current include path. For example, if the template function foo() is located in foo.h, the matching template definition file should be named foo.cc or some other recognizable source-file extension. The template definition file must be located in one of the normal include directories or in the same directory as its matching header file. Source File Extensions
You can specify different source file extensions for the compiler to search for when it is using its default Cfront-style source-file-locator mechanism. The format is:
extensions "ext-list"; |
The ext-list is a list of extensions for valid source files in a space-separated format such as:
extensions ".CC .c .cc .cpp"; |
In the absence of this entry from the options file, the valid extensions for which the compiler searches are .cc, .c, .cpp, .C, and .cxx. (See "Options File" on page 83.)
Definitions Search Path
As an alternative to the normal search path set with -I, you may specify a search directory for template definition files with the option -ptidirectory. Multiple -pti flags will define multiple search directories, i.e. a search path. If -ptidirectory is used, the compiler looks for template definition files on this path and ignores the -I flag. It is recommended, however, that you use the -I flag instead of the -ptidirectory flag, since the -ptidirectory flag complicates the search rules for source files. Options File Definition Entries
Definition source file locations may be explicitly specified with the definition option file entry. The definition entry is provided for those cases when the template declaration and definition file names do not follow the standard Cfront-style conventions. See "Source File Location Conventions" on page 88. The entry syntax is:
definition name in "file-1",[ "file-2" ..., "file-n"] [nocheck "options"]; |
The name field indicates the template for which the option entry is valid. Only one definition entry per name is allowed. That name must be a simple name; qualified names are not allowed. Parentheses, return types and parameter lists are not allowed. Regardless of the return type or parameters, only the name itself counts. As a consequence, a definition entry may apply to several (possibly overloaded) templates.
foo.cc |
template <class T> T foo( T t ) { } |
Template.opt |
definition foo in "foo.cc"; |
The following example shows the definition of static data members and the use of simple names.
foo.h |
template <class T> class foo { static T* fooref; }; |
foo_statics.cc |
#include "foo.h" template <class T> T* foo<T>::fooref = 0 |
Template.opt |
definition fooref in "foo_statics.cc"; |
The name provided for the definition of fooref is a simple name and not a qualified name (such as foo::fooref). The reason for the definition entry is that the file name is not foo.cc (or some other recognizable extension) and cannot be located using the default Cfront-style search rules. See "Source File Location Conventions" on page 88.
foo.h |
template <class T> class foo { T* foofunc(T); }; |
foo_funcs.cc |
#include "foo.h" template <class T> T* foo<T>::foofunc(T t) {} |
Template.opt |
definition foofunc in "foo_funcs.cc"; |
The following example shows the definition of template functions in two different source files.
In this example, the compiler must be able to find both definitions of the overloaded function func(). The definition entry tells the compiler where to find the appropriate function definitions.
Template Instance Automatic Consistency
The template database manager ensures that the state of the files in the database is consistent and up-to-date with your source files. Options File Nocheck Field
Sometimes recompiling is unnecessary when certain compilation flags change. You can avoid unnecessary recompilation using the nocheck field of the definition option file entry, which tells the compiler and template database manager to ignore certain options when checking dependencies. If you do not want the compiler to reinstantiate a template function because of the addition or deletion of a specific command-line flag, you should add that flag here. The entry syntax is:
definition name in "file-1"[, "file-2" ..., "file-n"] [nocheck "options"]; |
The name and file list fields are described in "Template Definition Searching" on page 87. The options themselves must be enclosed in quotes (" ").
foo.cc |
template <class T> T foo( T t ) {} |
Template.opt |
definition foo in "foo.cc" nocheck "-g"; |
Template Specialization, Deprecated Method
Until recently, the C++ language provided no mechanism for specializing templates, so each compiler provided its own mechanism. This section describes the specialization of templates using the mechanism of previous versions of the C++ compilers.
Note - Since this mechanism is supported in this release of the C++ compiler, but may not be supported in future releases, it is referred to as "deprecated." Avoid this mechanism for new code, and migrate existing code to the standard mechanism. (See "Template Specialization, Standard Method" on page 74.)
Specialized Template Definitions
The following example shows a specialized constructor that creates an array of character strings of fixed size:
void Array<char*>::Array( int sz ) { size = sz; data = new char * [size]; for ( int i = 0; i < size; i++ ) data[i] = new char[128]; } |
Array<int> IntArray( 10 ); // The compiler instantiates an Array<char*> CharStarArray( 10 ); // The compiler uses the |
The following example shows specialization of template functions:
Options File Specialization Entries
The special entry specifies to the compiler that a given function is a specialization and should not be instantiated when encountered. When using the compile-time instantiation method, preregister specializations with an entry in the options file. The syntax is:
special declaration; |
foo.h: |
template <class T> T foo( T t ) { }; |
main.cc: |
#include "foo.h" |
Template.opt: |
special foo(int); |
In the preceding example, when the compiler compiles main.cc, the specialized version of foo is correctly used because the compiler has seen its definition. When file.cc is compiled, however, the compiler instantiates its own version of foo because it doesn't know foo exists in main.cc. In most cases, this process results in a multiply-defined symbol during the link, but in some cases (especially libraries), the wrong function may be used, resulting in runtime errors. If you use specialized versions of a function, you should register those specializations.
foo.h |
template <classT> T foo( T t ) {} |
main.cc |
#include "foo.h" int foo( int i ) {} char* foo( char* p ) {} |
Template.opt |
special foo(int); special foo(char*); |
To specialize a template class, include the template arguments in the special entry:
foo.h |
template <class T> class Foo { ... various members ... }; |
main.cc |
#include "foo.h" int main( ) { Foo<int> bar; return 0; } |
Template.opt |
special class Foo<int>; |
foo.h |
template <class T> class Foo { public: static T func(T); }; |
main.cc |
#include "foo.h" int main( ) { Foo<int> bar; return 0; } |
Template.opt |
special static Foo<int>::func(int); |