7

Recently when I was trying to optimize my include hierarchy I stumbled upon the file a.hpp:

template<class T>
class A
{
  using t = typename T::a_t;
};

class B;

extern template class A<B>;

which seems to be ill-formed. In fact it seems as if the extern template statement at the end causes an instantiation of A<B> which causes the compiler to complain about an incomplete type.

My goal would have been to define A<B> in a.cpp:

#include <b.hpp>
template class A<B>;

This way I avoid having to include b.hpp from a.hpp which seems like a good idea to reduce compile time. However it does not work (a.hpp on itself doesn't compile!) Is there a better way of doing this?

Note: Of course I could just not use explicit template instantiation but this is not what I want! I would like to "precompile" A<B> to save compilation time if it were used, but if A<B> is not used I don't want to include b.hpp in every file that uses a.hpp!

craffael
  • 373
  • 1
  • 10
  • which compiler(s) are you using? – Ben Jan 07 '16 at 15:07
  • I tried clang 3.6.1 and gcc 4.8.2... – craffael Jan 07 '16 at 15:15
  • "it does not work". Please elaborate what you mean by that. Do you mean that it no longer compiles? Or do you mean that it just doesn't reduce compilation time like you hoped? – AndyG Jan 08 '16 at 05:20
  • I updated the question. With 'it does not work' I mean that if a source file, say `main.cpp`, includes just `a.hpp` but not `b.hpp` then `main.cpp` doesn't compile because type `B` is incomplete. – craffael Jan 08 '16 at 09:12

3 Answers3

2

The extern template declaration prevents the instantiation of member function bodies, but it forces the instantiation of the class definition, since the compiler needs that anyway, and the class body needs a full definition of the template argument since it accesses its members. I'm afraid that hiding B's body from users of A<B> is not possible.

extern template is an optimization, but it doesn't change the fundamental workings of the instantiation mechanism.

Sebastian Redl
  • 69,373
  • 8
  • 123
  • 157
0

The final extern template class A is telling the compiler that there is in some compilation unit a declaration of such template specialization. The compiler goes on and then the linker should complain about not finding the correct class. It is not ill formed; it depends on the use case. You can define in a separate cpp file the template A. This will obviously just reduce a bit the compile time if you compile it over and over. You can do different structures:

one a.hpp with just the class A template.

one b.cpp file with the class B along with its .h file. (is it a template?)

b.cpp includes a.hpp and inside you make an explicite template instantiation template class A; (not with extern).

At this point whenever you need to use that template you can just write

extern template class A;

in your file and link the compiled b.cpp file. If you include the a.hpp file since you still need the template you won't recompile it since you have the extern command.

CodingJack
  • 81
  • 2
  • Are you suggesting that I move the `extern template class A` from `a.hpp` to say `main.cpp` which includes then `b.hpp` before `a.hpp`? – craffael Jan 07 '16 at 16:32
  • is also b a template class? Yes you have to move it from there. It depends on who uses the A template. Put it in that file and the template won't be recompiled since you are explicitly telling the compiler that the class is available and compiled in another translation unit. You have to explicitly instantiate the template in another file like – CodingJack Jan 08 '16 at 15:51
0

From http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2003/n1448.pdf

The extern specifier used to declare an explicit instantiation of a class template only suppresses explicit instantiations of definitions of member functions and static data members not previously specialized in the translation unit containing the declaration.

Thus, if a definition of B is required in A, you cannot use an extern template without knowledge of B. You could of course try to get rid of that requirement. In the given case, you could remove the using t declaration and have a meta function to yield that type:

template<typename T>
struct get_a_t;

template<typename T>
struct get_a_t<A<T>>
{
   using type = typename T::a_t;
};

Not sure it that is feasible in your case. As soon as A needs to store a B or a B::a_t, you need B. References and pointers would be OK, though.

Rumburak
  • 3,416
  • 16
  • 27
  • Thanks a lot for the reference to the standard. Your solution with the meta-function is in my case not feasible; it would complicate things way too much. It seems that there is not really a nice solution to my problem so stopped using explicit template instantations for cases that I described above. This increases the compile time but leads to a much cleaner include hierarchy. – craffael Jan 25 '16 at 12:25