"Does that mean that when writing a template class, I should just write it all in the header file? What do C++ programmers do in these cases?"
What you can do (and is wide spread/popular practice), is separate implementation code out to special template implementation files, that will in turn be in íncluded by the template headers, containing the declarations.
The gain of this technique is considered little for most of the cases, though it has it's points, if you want to hide the implementation details and not spill header files to become large.
The point is not to have the template definition code, or specializations in separate translation units, such these can be seen directly by other translation units, including the template header file.
The common pattern is
MyTemplate.h
#if !defined(MYTEMPLATE_H_)
#define MYTEMPLATE_H_
namespace myspace {
template <typename T>
class MyTemplate {
public:
void foo(T value);
};
#include "MyTemplate.tcc"
}
#endif // MYTEMPLATE_H_
MyTemplate.tcc
// NOTE: There aren't header guards for this file intentionally!
template<typename T>
void MyTemplate<T>::foo(T value) {
// ...
}
Other popular extensions for template implementation files are .icc
, .ipp
, .impl
. Just important, it shouldn't be .cpp
, since most IDE's or build system frameworks will track this as translation unit, unless it's explicitly excluded (here's a sample why).
"So simply instead of the .cpp
#include
ing the header, the header #include
s the .tpp
(which contains the implementations)?"
Template classes work a bit differently regarding the ODR (one definition rule). Regular header files, that provide class declarations shouldn't contain implementations because the ODR would be violated when these are included from different translation units:
MyClass.h
class MyClass {
public:
void foo(int);
};
// Violates ODR if MyClass.h is included from different translation units (.cpp files)
void MyClass::foo(int param) {
}
The templated version
template<typename T>
class MyClass {
public:
void foo(T);
};
// Doesn't violate ODR if MyClass.h is included from different translation units
// (.cpp files), since the template parameter isn't instatiated here yet.
template<typename T>
void MyClass<T>::foo(T param) {
}
As soon one translation unit instantiated something like MyClass<int>
, other translation units instatiating the same template signature, will use the 1st one seen.
The latter implementation part could be replaced with an #include "MyClass.impl"
that contains that code, if you think it messes up readability or maintainability of your header file too much.
As a minor drawback of the #include "MyClass.tcc"
technique, you should notice, that most of the popular IDE's handle syntax highlighting and intellisense poorly for these kind of template implementation files.