Most of our classes are templated, in particular those carrying the bulk of the functionality, e.g., CompleterBase, HYBCompleter, CompletionServer etc. The standard procedure would be to put the definitions for these classes in the .h files along with the declarations. However, that has the unfortunate consequence of very long compile times: any small change in any of the definitions, and the whole thing has to be recompiled from scratch.
We avoid this by what is known as explicit instantiation: We put the definitions in a .cpp file as one would for non-templated classes and we explicitly state which instantiations we are going to need. For some reason that is not completely clear to me, it is crucial to put all th explicit instantiations at the very end. (Not doing so works sometimes and sometimes not, I couldn't really figure out why and when.) For example, at the end of CompleterBase.cpp we write
template class CompleterBase<SOME_MODE>;
In case there are also templated member functions ,they have to be dealt with as follows (again at the end of the respective .cpp file):
template void CompleterBase<SOME_MODE>::intersect<T1, T2, T3, T4>(... list of argument types as usual ...);
In the case of operators, the statements can look a but peculiar
template ostream& operator<<<int>(ostream&, int);
And for functors one almost needs to be a magician to write it the correct way
template ostream& operator<<<ios_base&(*)(ios_base&)>(ostream& os, ios_base&(*)(ios_base&))
Calling a templated member function of a templated base class
In HYBCompleterBase we have the following situation: we want to call a templated method (intersect) of the base class (CompleterBase), which is templated too. However, the following produces a compiler error
CompleterBase<MODE>::intersect<T1, T2, T3, T4>(...) // does NOT compile: syntax error before <
Our workaround for quite some time has been to make he arguments of intersect such that the template parameters can be unambiguously deduced from them, and we can write something like
CompleterBase<MODE>::intersect(...) // hack to avoid specifying template parameters for intersect, does not compile with g++-3.4 and upwards
but (a) this required the introduction of ugly extra classes (in case the template parameter is just some int), and (b) it doesn't compile with g++ version 3.4 or higher. I finally figures out that the correct way to write what we want is
CompleterBase<MODE>::template intersect<T1, T2, T3, T4>(...) // looks strange but that's the correct way!
Apparently, without the template keyword, the compiler is not able to figure out whether the < after the intersect is meant to be the comparison operator or the beginning of a template parameter specification.