G++ Template Notes:

There is now a mechanism which controls exactly when templates are expanded, so that you can reduce memory usage and program size and also instantiate them exactly once. You can control this mechanism with the option `-fexternal-templates' and its corresponding negation `-fno-external-templates'. Without this feature, space consumed by template instantiations can grow unacceptably in large-scale projects with many different source files. The default is `-fno-external-templates'. You do not need to use the `-fexternal-templates' option when compiling a file that does not define and instantiate templates used in other files, even if those files are compiled with `-fexternal-templates'. The only side effect is an increase in object size for each file that was compiled without `-fexternal-templates'. When your code is compiled with `-fexternal-templates', all template instantiations are external; this requires that the templates be under the control of `#pragma interface' and `#pragma implementation'. All instantiations that will be needed should be in the implementation file; you can do this with a typedef that references the instantiation needed. Conversely, when you compile using the option `-fno-external-templates', all template instantiations are explicitly internal. `-fexternal-templates' also allows you to finally separate class template function definitions from their declarations, thus speeding up compilation times for every file that includes the template declaration. Now you can have tens or even hundreds of lines in template declarations, and thousands or tens of thousands of lines in template definitions, with the definitions only going through the compiler once instead of once for each source file. It is important to note that you must remember to externally instantiate all templates that are used from template declarations in interface files. If you forget to do this, unresolved externals will occur. In the example below, the object file generated (`example.o') will contain the global instantiation for `Stack<int>'. If other types of `Stack' are needed, they can be added to `example.cc' or placed in a new file, in the same spirit as `example.cc'. foo.h:

#pragma interface "foo.h"
template<class T>
class Stack {
  static int statc;
  static T statc2;
  Stack() { }
  virtual ~Stack() { }
  int bar();
};
example.cc:
#pragma implementation "foo.h"
#include "foo.h"

typedef Stack<int> t;
int Stack<int>::statc;
int Stack<int>::statc2;
int Stack<int>::bar() { }
Note that using `-fexternal-templates' does not reduce memory usage from completely different instantiations (`Stack<Name>' vs. `Stack<Net_Connection>'), but only collapses different occurrences of `Stack<Name>' so that only one `Stack<Name>' is generated. `-falt-external-templates' selects a slight variation in the semantics described above (incidentally, you need not specify both options; `-falt-external-templates' implies `-fexternal-templates'). With `-fexternal-templates', the compiler emits a definition in the implementation file that includes the header definition, even if instantiation is triggered from a different implementation file (e.g. with a template that uses another template). With `-falt-external-templates', the definition always goes in the implementation file that triggers instantiation. For instance, with these two header files---
`a.h':
#pragma interface
template <class T> class A { ... };

`b.h':
#pragma interface
class B { ... };
void f (A<B>);
Under `-fexternal-templates', the definition of `A<B>' ends up in the implementation file that includes `a.h'. Under `-falt-external-templates', the same definition ends up in the implementation file that includes `b.h'.

You can control explicitly where a template is instantiated, without having to use the template to get an instantiation. To instantiate a class template explicitly, write `template class name<paramvals>', where paramvals is a list of values for the template parameters. For example, you might write

template class A<int>
Similarly, to instantiate a function template explicitly, write `template fnsign' where fnsign is the particular function signature you need. For example, you might write
template void foo (int, int)
This syntax for explicit template instantiation agrees with recent extensions to the draft ANSI standard.

The compiler's actions on ANSI-related warnings and errors have been further enhanced. The `-pedantic-errors' option produces error messages in a number of new situations: using return in a non-void function (one returning a value); declaring a local variable that shadows a parameter (e.g., the function takes an argument `a', and has a local variable `a'); and use of the `asm' keyword. Finally, the compiler by default now issues a warning when converting from an int to an enumerated type. This is likely to cause many new warnings in code that hadn't triggered them before. For example, when you compile this code,

enum boolean { false, true };
void
f ()
{
  boolean x;

  x = 1; //assigning an int to an enum now triggers a warning
}
you should see the warning "anachronistic conversion from integer type to enumeral type `boolean'". Instead of assigning the value 1, assign the original enumerated value `true'.

Notes taken from James Stewart tutorial

Using Template Classes in Your Code

Template classes are used much like normal classes. The only difference is that declaration of a class instance must include the template type, T. This is great from the point of view of someone using the template, since they can easily use the template on various data types.
#include "list.h"

main()
{
  List< int > list1;
  List< float > list2;

  list1.add( 5 );
  list2.add( 2.7 );

  cout << list1.remove();

  ... etc ...
}

What File Contains the Template?

Typically, you put all your template code in a single *.h file and include it as shown above. This file should contain the class declaration and the function definitions that appear outside the class declaration.

Why all in one file? In the example above, C++ had to create code for the add(), remove(), and empty() functions for both ints and floats. To create the code, C++ needed to know the function definitions. Since C++ typically creates functions for a template class when it sees a declaration of an instance of that class, it must know the function definitions at that time. Thus, they must appear in the *.h file.

A More Efficient Alternative

If you declare an instance of List< int > in several different *.C files, C++ will create code for add(), remove(), and empty() in each of those *.C files. It doesn't know any better, since the C++ files are compiled separately. This is wasteful.

The GNU version of C++ provides a mechanism to avoid this. Here's an except from the man page for gcc, describing one of the switches that can be included on the command line of g++:

-fexternal-templates

  Produce smaller code for template declarations, by generating only a  single
  copy  of each template function where it is defined (C++ only).  To use this
  option successfully, you must also mark all files that  use  templates  with
  either  `#pragma  implementation'  (the  definition)  or `#pragma interface'
  (declarations).

  When your code is compiled with `-fexternal-templates', all template instan-
  tiations are external.  You must arrange for all necessary instantiations to
  appear in the implementation file; you can do this with a typedef that  ref-
  erences  each  instantiation needed.  Conversely, when you compile using the
  default option `-fno-external-templates', all  template  instantiations  are
  explicitly internal.
This means that if you compile with
g++ -fexternal-templates ...
and include the line
#pragma interface
in your *.h file, C++ will not create code when it encounters an instance of a template class.

However, the code must be created somewhere. This means that you need another *.C file (typically with the same base name as your *.h file) that causes the code to be created. Such a *.C file must contain

#pragma implementation
and must include a typedef for each type of template you need. For example, assuming the list.h file has #pragma interface in it, we cause code to be created for two types of list by compiling the following list.C file:
#pragma implementation
#include "list.h"

typedef List< int > dummy1;
typedef List< float > dummy2;
The object file, list.o, will contain code for the functions add(), remove(), and empty() for both types of List. In the elevator assignment, you were given a complete definition of a List template class in list.h and list.C.