Using Templates in g++

A confusing issue with templates is understanding how and when the machine code for a particular instance of the template is actually generated.  The default behavior of the g++ compiler when compiling a file is to generate code for any use of a template mentioned in that file.  For example, if the file MyModule.cc contains the line

      template class Stack<int> MyStack;

then MyModule.o will contain all the code for the <int> instantiation of the Stack class.  This has two bad effects:

1. The body of Stack.cc must be included in MyModule.cc, as well as the header Stack.h.  (Sometimes this is done by having Stack.h include Stack.cc; most of the downloadable code supplied from the Weiss textbook web site does this.)

2. If you have several different source files that mention the same template instance, then the generated code is duplicated in every object file.  This duplication not only is wasteful but can cause errors when the compiler tries to link the object files together, because the same symbols are defined more than once.

The solution is to include the .h file in all of the .cc files using the class, and to instantiate the template in only one compiled .cc file.  This is done as follows:

In your ordinary source files include only the header .h files for the templates used (and be sure those .h files do not include the corresponding .cc files).  Then compile those files to object files using a compiler flag “-fno-implicit-templates” that prevents the default automatic code generation behavior.  For example:

      g++ -fno-implicit-templates –c MyModule.cc 

Then create one or more special source files that explicitly instantiate the templates that are needed anywhere in your program.  This file should include both the header .h and body .cc of each such template.  A template is explicitly instantiated by mentioning it without specifying any variables of that type.  For example, such a special file MyInstantiations.cc could be:

#include "Stack.h"
#include "Stack.cc"
template class Stack<int>;
 

The Makefile for the example just given could be the following:

 
      MyTest:  MyModule1.o MyModule2.o MyInstantiations.o
            g++ MyModule1.o MyModule2.o MyInstantiations.o –o MyTest
      MyModule1.o:  MyModule1.cc Stack.h
            g++ -fno-implicit-templates –c MyModule1.cc
      MyModule2.o:  MyModule2.cc Stack.h
            g++ -fno-implicit-templates –c MyModule2.cc
      MyInstantiations.o:  MyInstantiations.cc Stack.h Stack.cc
            g++ –c MyInstantiations.cc
 

Programming Style Rules

The most important factors for good code are:

  1. Clarity – code should be human readable!
  2. Robustness – test for and handle bad input, array overflow, etc.  If the code you turn it does not work when we try to run it, you will loose credit – we will not debug your code.
  3. Correctness
  4. Asymptotic complexity – this is actually part of correctness:  the theoretical scaling of the algorithm you implemented with the size of the data should be correct:  If we ask for an O( log N ) algorithm and you implement one that is O( N ), then it is not entirely correct.
  5. Worrying about lower-level efficiency (e.g., saving a constant number of machine cycles by using a pointer rather than an array reference) is strongly discouraged whenever it interferes with factors (a)-(d).

General Advice

  1. Write and debug classes without using templates first, and then convert to templates.
  2. Avoid function templates, instead use only template classes; the current C++ standard disallows function templates.
  3. Recommended book:  C++ Distilled: A Concise ANSI/ISO Reference and Style Guide, Ira Pohl, Addison-Wesley, 1997.