Question

Summary
This question is about achieving separate compilation of a single template class instantiation in a several different translation units.

Question
For non-template classes one can put definitions in several .cpp files and compile them separately. For instance:

file A.h:

class A {
public:
  void func1();
  void func2();
  void func3() { /* defined in class declaration */}
}

file A1.cpp:

void A::func1() { /* do smth */ }

file A2.cpp:

void A::func2() { /* do smth else */ }

Now I tried to do something similar with template classes. Since I know exactly which instances I will need, I'm explicitly instantiating templates. I'm compiling each instantiation separately, because member functions contain rather large mathematical expressions, which could slow down compiler considerably on high optimization levels. So I tried the following:

file TA.h:

template <typename T>
class TA {
public:
  void func1();
  void func2();
  void func3() { /* defined in class declaration */}
}

file TA1.cpp:

template <typename T>
void TA<T>::func1() { /* do smth */ }
template class TA<sometype>;

file TA2.cpp:

template <typename T>
void TA<T>::func2() { /* do smth else */ }
template class TA<sometype>;

It works with clang and GCC on Linux, but fails with GCC on Mac during linking during duplicate symbols error (in this example due to func3, which got instantiated in both TA1.cpp and TA2.cpp).

Then I stumbled upon this sentence in the standard:

C++11.14.7, paragraph 5:
For a given template and a given set of template-arguments,
-- an explicit instantiation definition shall appear at most once in a program,
-- ...

Does it mean that separate compilation of template classes is not possible (not allowed) even when using explicit instantiation (it is obviously not possible with implicit instantiation)?

PS I don't care since I've got my answer, but whoever thinks it is answered in here https://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file is wrong.

Was it helpful?

Solution

After another look at the standard, it seems to me that the only reasonable option is to use single explicit template class instantiation combined with explicit member function instantiations of a small number of "difficult" functions.

This (according to 14.7.2p9) will instantiate the class and all members which have been defined up to this point (which should include everything except "difficult" members). Then those selected members can be explicitly instantiated in other translation units containing their definitions.

That would make my example look like below (assuming that TA1.cpp contains easy functions and the only "difficult" function in TA is func2)

file TA1.cpp:

template <typename T>
void TA<T>::func1() { /* "simple" function definition */ }

template class TA<sometype>; /* expl. inst. of class */

file TA2.cpp:

template <typename T>
void TA<T>::func2() { /* "difficult" function definition */ }

template void TA<sometype>::func2(); /* expl. inst. of member */

This method requires us to write explicit instantiation definition for every "difficult" function, which is tedious but also makes us think twice whether we really want to keep it separately or not.

Disclaimer

When that can be useful? Not often. As other people here mentioned, it is not advised to split definitions of classes over several files. In my particular case "difficult" functions contain complicated mathematical operations on instances of non-trivial classes. C++ templates are not famous for fast compilation speeds, but in this case it was unbearable. These functions call each other which sends compiler on long and memory-consuming journey of expanding/inlining overloaded operators/templates/etc to optimize everything it sees, with pretty much zero improvement, but making compilation last for hours. This trick of isolating some functions in separate files speeds up compilation 20 times (and allows to parallelize it as well).

OTHER TIPS

Separate compilation of templates is tricky but allowed. What you cannot do is to explicitly instantiate the type in multiple translation units, in the same way that you cannot define the same function in two translation units.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top