16.3. Template Compilation Models
When the compiler sees a template definition, it does not generate code immediately. The compiler produces type-specific instances of the template only when it sees a use of the template, such as when a function template is called or an object of a class template is defined.
Ordinarily, when we call a function, the compiler needs to see only a declaration for the function. Similarly, when we define an object of class type, the class definition must be available, but the definitions of the member functions need not be present. As a result, we put class definitions and function declarations in header files and definitions of ordinary and class-member functions in source files.
Templates are different: To generate an instantiation, the compiler must have access to the source
code that defines the template. When we call a function template or a member function of a class
template, the compiler needs the function definition. It needs the code we normally put in the
source files.
Inclusion Compilation Model
In the inclusion compilation model , the compiler must see the definition for any template that is used. Typically, we make the definitions available by adding a #includedirective to the headers that declare function or class templates. That #includebrings in the source file(s) that contain the associated definitions:
// header file utlities.h
#ifndef UTLITIES_H // header gaurd (Section 2.9.2, p. 69)
#define UTLITIES_H
template <class T> int compare(const T&, const T&);
// other declarations
#include "utilities.cc" // get the definitions for compare etc.
#endif
// implemenatation file utlities.cc
template <class T> int compare(const T &v1, const T &v2)
{
if (v1 < v2) return -1;
if (v2 < v1) return 1;
return 0;
}
// other definitions
This strategy lets us maintain the separation of header files and implementation files but ensures that the compiler will see both files when compiling code that uses the templates.
Some, especially older, compilers that use the inclusion model may generate multiple instantiations. If two or more separately compiled source files use the same template, these compilers will generate an instantiation for the template in each file. Ordinarily, this approach implies that a given template will be instantiated more than once. At link time, or during a prelink phase, the compiler selects one instantiation, discarding the others. In such cases, compile-time performance can be significantly degraded if there are a lot of files that instantiate the same template. This compile-time degradation is unlikely to be a problem on modern computers for many applications. However, in the context of large systems, the compile-time hit may become important.
Such compilers often support mechanisms that avoid the compile-time overhead implicit in multiple instantiations of the same template. The way compilers optimize compile-time performance varies from one compiler to the next. If compile time for programs using templates is too burdensome, consult your compiler's user's guide to see what support your compiler offers to avoid redundant instantiations.
Separate Compilation Model
In the separate compilation model , the compiler keeps track of the associated template definitions for us. However, we must tell the compiler to remember a given template definition. We use the export keyword to do so.
The exportkeyword indicates that a given definition might be needed to generate instantiations in other files. A template may be defined as exported only once in a program. The compiler figures out how to locate the template definition when it needs to generate these instantiations. The export keyword need not appear on the template declaration.
Ordinarily, we indicate that a function template is exported as part of its definition. We do so by including the keyword exportbefore the templatekeyword:
// the template definition goes in a separately-compiled source file
export template <typename Type>
Type sum(Type t1, Type t2) /* ...*/
The declaration for this function template, should, as usual, be put in a header. The declaration
must not specify export.
Using exporton a class template is a bit more complicated. As usual, the class declaration must go in a header file. The class body in the header should not use the exportkeyword. If we used export in the header, then that header could be used by only one source file in the program.
Instead, we exportthe class in the class implementation file:
// class template header goes in shared header file
template <class Type> class Queue { ... };
// Queue.ccimplementation file declares Queue as exported
export template <class Type> class Queue;
#include "Queue.h"
// Queue member definitions
The members of an exported class are automatically declared as exported. It is also possible to declare individual members of a class template as exported. In this case, the keyword exportis not specified on the class template itself. It is specified only on the specific member definitions to be exported. The definition of exported member functions need not be visible when the member is used. The definitions of any nonexported member must be treated as in the inclusion model: The definition should be placed inside the header that defines the class template.