简体   繁体   中英

extern template & incomplete types

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 !

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.

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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM