3.5. Where's the Template?

C++ templates are the first language feature to require more intelligence from the environment than one usually finds on a UNIX system. Somehow the compiler and linker have to make sure that each template instance occurs exactly once in the executable if it is needed, and not at all otherwise. There are two basic approaches to this problem, which I will refer to as the Borland model and the Cfront model.

Borland model

Borland C++ solved the template instantiation problem by adding the code equivalent of common blocks to their linker; the compiler emits template instances in each translation unit that uses them, and the linker collapses them together. The advantage of this model is that the linker only has to consider the object files themselves; there is no external complexity to worry about. This disadvantage is that compilation time is increased because the template code is being compiled repeatedly. Code written for this model tends to include definitions of all templates in the header file, since they must be seen to be instantiated.

Cfront model

The AT&T C++ translator, Cfront, solved the template instantiation problem by creating the notion of a template repository, an automatically maintained place where template instances are stored. A more modern version of the repository works as follows: As individual object files are built, the compiler places any template definitions and instantiations encountered in the repository. At link time, the link wrapper adds in the objects in the repository and compiles any needed instances that were not previously emitted. The advantages of this model are more optimal compilation speed and the ability to use the system linker; to implement the Borland model a compiler vendor also needs to replace the linker. The disadvantages are vastly increased complexity, and thus potential for error; for some code this can be just as transparent, but in practice it can been very difficult to build multiple programs in one directory and one program in multiple directories. Code written for this model tends to separate definitions of non-inline member templates into a separate file, which should be compiled separately.

When used with GNU ld version 2.8 or later on an ELF system such as Linux/GNU or Solaris 2, or on Microsoft Windows, g++ supports the Borland model. On other systems, g++ implements neither automatic model.

A future version of g++ will support a hybrid model whereby the compiler will emit any instantiations for which the template definition is included in the compile, and store template definitions and instantiation context information into the object file for the rest. The link wrapper will extract that information as necessary and invoke the compiler to produce the remaining instantiations. The linker will then combine duplicate instantiations.

In the mean time, you have the following options for dealing with template instantiations:

  1. Compile your code with “-fno-implicit-templates” to disable the implicit generation of template instances, and explicitly instantiate all the ones you use. This approach requires more knowledge of exactly which instances you need than do the others, but it's less mysterious and allows greater control. You can scatter the explicit instantiations throughout your program, perhaps putting them in the translation units where the instances are used or the translation units that define the templates themselves; you can put all of the explicit instantiations you need into one big file; or you can create small files like

    #include "Foo.h"
    #include "Foo.cc"
    template class Foo<int>;
    template ostream& operator <<
                    (ostream&, const Foo<int>&);
    

    for each of the instances you need, and create a template instantiation library from those.

    If you are using Cfront-model code, you can probably get away with not using “-fno-implicit-templates” when compiling files that don't “#include” the member template definitions.

    If you use one big file to do the instantiations, you may want to compile it without “-fno-implicit-templates” so you get all of the instances required by your explicit instantiations (but not by any other files) without having to specify them as well.

    g++ has extended the template instantiation syntax outlined in the Working Paper to allow forward declaration of explicit instantiations, explicit instantiation of members of template classes and instantiation of the compiler support data for a template class (i.e. the vtable) without instantiating any of its members:

    extern template int max (int, int);
    template void Foo<int>::f ();
    inline template class Foo<int>;
    
  2. Do nothing. Pretend g++ does implement automatic instantiation management. Code written for the Borland model will work fine, but each translation unit will contain instances of each of the templates it uses. In a large program, this can lead to an unacceptable amount of code duplication.

  3. Add “#pragma interface” to all files containing template definitions. For each of these files, add “#pragma implementation "filename"” to the top of some “.C” file that “#include”s it. Then compile everything with “-fexternal-templates”. The templates will then only be expanded in the translation unit that implements them (i.e. has a “#pragma implementation” line for the file where they live); all other files will use external references. If you're lucky, everything should work properly. If you get undefined symbol errors, you need to make sure that each template instance that is used in the program is used in the file that implements that template. If you don't have any use for a particular instance in that file, you can just instantiate it explicitly, using the syntax from the latest C++ working paper:

    template class A<int>;
    template ostream& operator << (ostream&, const A<int>&);
    

    This strategy will work with code written for either model. If you are using code written for the Cfront model, the file containing a class template and the file containing its member templates should be implemented in the same translation unit.

    A slight variation on this approach is to instead use the flag “-falt-external-templates”; this flag causes template instances to be emitted in the translation unit that implements the header where they are first instantiated, rather than the one that implements the file where the templates are defined. This header must be the same in all translation units, or things are likely to break.

    See Section 3.4, for more discussion of these pragmas.