Section 16.3. Template Compilation Models


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.

Exercises Section 16.2.2

Exercise 16.23:

The library max function takes a single type parameter. Could you call max passing it an int and a double? If so, how? If not, why not?

Exercise 16.24:

In Section 16.2.1 (p. 638) we saw that the arguments to the version of compare that has a single template type parameter must match exactly. If we wanted to call the function with compatible types, such as int and short, we could use an explicit template argument to specify either int or short as the parameter type. Write a program that uses the version of compare that has one template parameter. Call compare using an explicit template argument that will let you pass arguments of type int and short.

Exercise 16.25:

Use an explicit template argument to make it sensible to call compare passing two string literals.

Exercise 16.26:

Given the following template definition for sum

      template <class T1, class T2, class T3> T1 sum(T2, T3); 

explain each of the following calls. Indicate which, if any, are errors. For each error, explain what is wrong.

      double dobj1, dobj2; float fobj1, fobj2; char cobj1, cobj2;      (a) sum(dobj1, dobj2);      (b) sum<double, double, double>(fobj1, fobj2);      (c) sum<int>(cobj1, cobj2);      (d) sum<double, ,double>(fobj2, dobj2); 


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.

Standard C++ defines two models for compiling template code. In each of these models, we structure our programs in largely the same way: Class definitions and function declarations go in header files, and function and member definitions go in source files. The two models differ in how the definitions from the source files are made available to the compiler. As of this writing, all compilers support the first model, known as the "inclusion" model; only some compilers support the second, "separate compilation" model.

To compile code that uses your own class and function templates, you must consult your compiler's user's guide to see how your compiler handles instantiation.



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 #include directive to the headers that declare function or class templates. That #include brings 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 export keyword 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 export before the template keyword:

      // 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 export on 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 export keyword. If we used export in the header, then that header could be used by only one source file in the program.

Instead, we export the 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 export is 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.

Exercises Section 16.3

Exercise 16.27:

Determine which compilation model your compiler uses. Write and call a function template to find the median value in a vector that holds objects of unknown type. (Note: The median is a value such that half the elements are larger than the median, and half are smaller.) Structure your program in the normal way: The function definition should go in one file, a declaration for it in a header, which the code that defines and uses the function template should include.

Exercise 16.28:

Where would you place the definitions for the member functions and static data members of your class templates if the compiler you use supports the separation compilation model? Explain why.

Exercise 16.29:

Where would you put those template member definitions if your compiler uses the inclusion model? Explain why.


Caution: Name Lookup in Class Templates

Compiling templates is a surprisingly difficult task. Fortunately, it is a task handled by compiler writers. Unfortunately, some of that complexity is pushed onto users of templates: Templates contain two kinds of names:

  1. Those that do not depend on a template parameter

  2. Those that do depend on a template parameter

It is up to the template designer to ensure that all names that do not depend on a template parameter are defined in the same scope as the template itself.

It is up to users of a template to ensure that declarations for all functions, types, and operators associated with the types used to instantiate the template are visible. This responsibility means that the user must ensure that these declarations are visible when a member of a class template or a function template is instantiated.

Both of these requirements are easily satisfied by well-structured programs that make appropriate use of headers. Authors of templates should provide a header that contains declarations for all the names used in the class template or in the definitions of its members. Before defining a template on a particular type or using a member of that template, the user must ensure that the header for the template type and the header that defines the type used as the element type are included.




C++ Primer
C Primer Plus (5th Edition)
ISBN: 0672326965
EAN: 2147483647
Year: 2006
Pages: 223
Authors: Stephen Prata

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net