(Edited: This is slightly more robust version which allows the implementation to be compiled into a separate .o file. The template should be explicitly instantiated at the end of the implementation cpp file. Perhaps this was only an issue for g++.)
You don't need to put the implementations in the header if you know which templates will be instantiated and can list them in the header implementation file. For example, if you know that you will only use int
and std::string
, then you can put this in the header file:
// test.h
template <class A>
class Test
{
public:
void f();
};
and put the implementation of f() into a normal test.cpp file:
// test.cpp
#include "test.h"
template <class A> void Test<A>::f() {
// implementation
}
template class Test<int>;
template class Test<string>;
Those last two lines explicitly instantiate the template classes. It is better to put this at the end of the implementation file, after it has seen the implementations of the member functions. Then you can compile it to a .o file g++ -c test.cpp
. This test.o file will contain complete implementations of both Test<int>
and Test<string>
and can be linked without any difficulty in the rest of your application.
It works, but is it a good idea? It depends on context. In many cases, this works very well. If you are writing a template for 'internal' use in your project, then you know which templates will be instantiated and which will not. But if instead you're making something available to the public which must be very flexible, then you will need to include the implementations in the header file.
A tip: Even if it's for public consumption, take a look at the methods and see if there are any methods whose parameters and return type are independent of the template parameters. If so, you could place them into a Base class as (pure) virtual functions. This Base class doesn't use any templates and hence you may be able to use this Base*
in much of your application (template <class A> class Test : public Base { ...
, allowing you to limit the reach of the templated code throughout your application. I found this useful recently when much of the underlying behaviour and construction of a class depended on the template parameters, but the interface to the already-constructed object did not depend on the template parameters.