Using Templates in Practice
1. Templates challenge the classic compiler-plus-linker model. Therefore there are different approaches to organize template code: the inclusion model, explicit instantiation, and the separation model
Most C and C++ programmers organize their nontemplate code largely as follows:
1) Classes and other types are entirely placed in header files.
2) For global variables and (noninline) functions, only a declaration is put in a header file, and the definition goes into a so-called dot-C file.
The works well: It makes the needed type definition easily available throughout the program and avoids duplicate definition errors on variables and functions from the linker.
With the convention in mind, we declare the template in a header file:
#ifndef MYFIRST_HPP
#define MYFIRST_HPP
![](/Images/OutliningIndicators/None.gif)
// declaration of template
template <typename T>
void print_typeof (T const&);
![](/Images/OutliningIndicators/None.gif)
#endif // MYFIRST_HPPThe implementation of the function is placed in a dot-C file:
#include <iostream>
#include <typeinfo>
#include "myfirst.hpp"
![](/Images/OutliningIndicators/None.gif)
// implementation/definition of template
template <typename T>
void print_typeof (T const& x)
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](/Images/OutliningIndicators/ContractedBlock.gif)
{
std::cout << typeid(x).name() << std::endl;
}Finally, we use the template in another dot-C file, into which our template declaration is #include:
#include "myfirst.hpp"
![](/Images/OutliningIndicators/None.gif)
// use of the template
int main()
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](/Images/OutliningIndicators/ContractedBlock.gif)
{
double ice = 3.0;
print_typeof(ice); // call function template for type double
}A C++ compiler will most likely accept this program without any problems,but
the linker will probably report an error,
implying that there is no definition of the function print_typeof().
In order for a template to be instantiated, the compiler
must know which definition should be instantiated and for what template arguments it should be instantiated. Unfortunately, these
two pieces of information are in files that are compiled separatedly. Therefore, when our compiler sees the call to print_typeof() but has no definition in sight to instantiate this function for
double, it just assumes that such a definition is provided elsewhere and creates a reference(
for the linker to resolve,linker) to that definition. On the other hand, when the compiler processes the file myfirst.cpp, it has no indication at that point that it must instantiate the template definition it contains for specific arguments.
The Inclusion Model
Rewrite the header-file, including template definition.
#ifndef MYFIRST_HPP
#define MYFIRST_HPP
![](/Images/OutliningIndicators/None.gif)
#include <iostream>
#include <typeinfo>
![](/Images/OutliningIndicators/None.gif)
// declaration of template
template <typename T>
void print_typeof (T const&);
![](/Images/OutliningIndicators/None.gif)
// implementation/definition of template
template <typename T>
void print_typeof (T const& x)
![](/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](/Images/OutliningIndicators/ContractedBlock.gif)
{
std::cout << typeid(x).name() << std::endl;
}
![](/Images/OutliningIndicators/None.gif)
#endif // MYFIRST_HPPdisadvantage: the cost is not the result of size of the template definition itself, but the result of the fact that we must also include the header used by the definition of our template-in the case <iostream> and <typeinfo>.\
Explicit Instantiation
To avoid above linker error we can add the following file to our program:
#include "myfirst.cpp"
![](/Images/OutliningIndicators/None.gif)
// explicitly instantiate print_typeof() for type double
template void print_typeof<double>(double const&);disadvantage: We must carefully keep track of which entities to instantiate. For large projects this quickly becomes an excessive burden.
advantage:
the instantiation can be tuned to the needs of the program.
The overhead of large header is avoided.
The source code of template definition can be kept hidden, but then no additional instantiations can be created by a client program.
Finally, for some applications it can be useful to control the exact location(that is, the object file)of a template instance.
Separation model
2. Usually you should use the inclusion model
3. By separating template code into different header files for declarations and definitions, you can more easily switch between the inclusion model and explicit instantiation
4. The C++ standard defines a separate compilation model for templates(using the keyword export). It is not yet widely available, however.
5. To take advantage of precompiled headers, be sure to keep the same order for #include directives.
6. Debuggin code with templates can be challenging
7. Template instances may have very long names