5

Is putting an extern template in a header file and then do the explicit template instantiation in a unit compilation file valid ?

For example in the compiling example for g++, is this working to avoid the instancation of nothing<int> twice ? Why doesn't anybody write it like this and prefer to copy pase the extern template line in each .cpp file ?

A.hpp:

#ifndef HEADERC_A
#define HEADERC_A

template< typename T > struct nothing {};
extern template struct nothing<int>;

#endif

A.cpp:

#include "A.hpp"

template struct nothing<int>;

main.cpp:

#include "A.hpp"
#include <iostream>

int main()
{
    nothing<int> n;
    return 0;
}
Lærne
  • 3,010
  • 1
  • 22
  • 33
  • *"and prefer to copy pase the extern template line in each .cpp file"* Why would anyone want to do that? / In what context have you seen this? – dyp Jan 03 '15 at 19:35
  • In no concrete production code, but every example I've seen so far declare the `extern template` into a .cpp file. See for instance, https://isocpp.org/wiki/faq/cpp11-language-templates#extern-templates. – Lærne Jan 03 '15 at 19:47
  • I am not sure if you get any benefits from explicitly instantiating an empty class as shown in your example. It does *not* prevent implicit instantiation of `nothing` in `main.cpp` according to [temp.explicit]/10. It would prevent the implicit instantiation of any (non-inline) member functions of that class, for example. – dyp Jan 03 '15 at 20:16
  • It's a dummy example to avoid posting bloats of code :) Declaring a regular `struct` in a header file does not prevent instantiation either, so I don't mind. If I want to prevent both, I confidently feel that typing `template struct nothing;` and nothing more is sufficient. – Lærne Jan 03 '15 at 20:29
  • *"Declaring a regular struct in a header file does not prevent instantiation either, so I don't mind"* Exactly. But *"`template struct nothing;` and nothing more is sufficient."* this would prevent `nothing m;` from compiling in `main.cpp`. That is, as far as I can see, an explicit instantiation declaration for a class template is only a convenience for explicitly instantiating the declarations of all member functions, static data members etc. – dyp Jan 03 '15 at 20:41
  • `nothing n;` is implicit instanciation if I understand well those cryptic lines : `Unless a class template specialization has been explicitly instantiated (14.7.2) or explicitly specialized (14.7.3), the class template specialization is implicitly instantiated when the specialization is referenced in a context that requires a completely-defined object type or when the completeness of the class type affects the semantics of the program`. – Lærne Jan 03 '15 at 20:55
  • And the declaration prevents the implicit instantiation if I understand this: `An entity that is the subject of an explicit instantiation declaration and that is also used in a way that would otherwise cause an implicit instantiation (14.7.1) in the translation unit shall be the subject of an explicit instantiation definition somewhere in the program; otherwise the program is ill-formed.` – Lærne Jan 03 '15 at 20:57
  • [temp.explicit]/10 from the C++11 Standard: *"Except for inline functions and class template specializations, explicit instantiation declarations have the effect of suppressing the implicit instantiation of the entity to which they refer."* I don't see any other way for this to work: You need the definition of a type to know its data members, base classes and virtual functions (which determine the size of an object of that type) to construct an object of that type. The compiler needs that knowledge in the translation unit of `main.cpp`. – dyp Jan 03 '15 at 21:02
  • Yes but it doesn't need to build the member function code. We argue in agreement :) – Lærne Jan 03 '15 at 21:36
  • Indeed, if your *"And the declaration prevents the implicit instantiation"* was referring to member functions, not the class template specialization `nothing`. (The class template is instantiated despite the explicit instantiation declaration in `main.cpp`. Its member functions are *not* implicitly instantiated in `main.cpp`, though.) – dyp Jan 03 '15 at 21:38

2 Answers2

3

Well, this is certainly "valid" insofar as gcc will compile this, and do pretty much what you expect to happen.

As far as why doesn't everyone to do this, well, once you go beyond a trivial situation like this, and you start managing a large collection of widely used templates, it will quickly reach the point where it simply becomes not practical to keep track of every parameter that each one of your templates gets used with, so that it can be instantiated explicitly, in this manner.

It's going to be much easier for the compiler to keep track of it, for you.

Sam Varshavchik
  • 114,536
  • 5
  • 94
  • 148
  • Okay, are other compiler as compliant as well, like clang++ and msvc? Also in my actual project, it's a very used data structure and I don't want to keep track of which templates I instanciated and I should `extern template`-declare them in almost every single .cpp file I have. I'm just interested in two types for my template that has been properly `typedef`ed and I don't want to redeclare them each time. – Lærne Jan 03 '15 at 19:33
  • I was able to confirm the details of gcc's implementation of templates simply by reading gcc's documentation. These kinds of things are not some kind of deeply-held secrets. gcc's documentation, for example, is at https://gcc.gnu.org/onlinedocs/gcc/Template-Instantiation.html and I'm sure that the details of clang's and MSVC's template implementation can also be found in their respective documentation. I don't use those compilers so I don't know where their documentation is, but since you're using them, I'm sure you can look them up and get the answer by yourself. – Sam Varshavchik Jan 04 '15 at 02:17
1

As previously stated this is a perfectly valid use case and if it fits your programming model then you should use it. But buyer beware:

There are several reasons why extern templates are not commonly declared in header files and then explicitly instantiated in the cpp files.

A very common model for implementing template classes/functions is to place the definitions in the header file and the implementation in an "inl" or other named file. But then include that file at the bottom of the header file. There are reams of code that use this approach to resolving the template/header/implementation separation problem. Putting an "extern" at the top of the implementation makes the code much easier to read and maintain, especially when multiple classes get involved. Heres an example:

A.hpp

#pragma once
template< typename T > struct nothing {
  void donothing(T input); // fastest func around
};
#include "A.inl"

A.inl

// does NOT include A.hpp
extern template struct nothing<int>; // save time and space
template<typename T> nothing<T>::donothing { return; }

Instance.h

#include "A.hpp"
template struct nothing<int>; // compiler generates code now

But there is a hidden caveat in all this...

If this gets implemented as you suggest then what happens when another person comes along and wants:

nothing<float> mynothing;

The compiler will see the header file but never find an implementation for float. So it may compile just fine, but at link time there will be unresolvable symbols. So they try this:

template struct nothing<float>;
nothing<float> mynothing;

WRONG! Now the compiler can't find the implementation and all you get is MORE errors.

Now you could go back to your A.cpp file and add another instance for float...can you say maintenance, headache, carpal tunnel nightmare? With the commonly used solution you get to have your cake and eat it to. (mostly)

Now you may be thinking why even bother with the extern? Because as your post implies there is a typical use case where most of the time "nothing" will be used with an int template type. Having this occur in potentially hundreds of files can lead to serious compile time and code size ramifications.

Why doesn't the standards committee do something about this mess? They did! They added extern templates! In all fairness it is a difficult issue to resolve after the fact.