As its been said, sometimes you do want an include file to be invoked more than once; and there may be many situations where this is desirable.
One example where this comes in useful is for optimizing instantiations of large, complicated templates. Consider some typical large, complicated template class
template<typename T> class ComplicatedTemplate {
// ... Boring stuff goes here
};
Having this large template instantiated and compiled in every translation unit, for the same template type, over and over again, gets really old. It slows down a compile, and needlessly bloats every object module, only to have the linker deal with stripping out a ton of duplicate template instantiations. It's a lot of wasted work.
Many compilers offer ways to control template instantiations. The exact details can vary, sometimes, but I'll use the typical approach, as used by gcc, which you can read about here:
https://gcc.gnu.org/onlinedocs/gcc/Template-Instantiation.html
Say, you'd want to burn some CPU time instantiating ComplicatedTemplate<std::vector<int>>
, ComplicatedTemplate<std::vector<char>>
, maybe ComplicatedTemplate<std::string<std::string>>
in some translation unit called "complicated.cpp", and just declare them extern in the header file.
Ok, so you end up with this in complicated_template.H
template<typename T> class ComplicatedTemplate {
// ... Boring stuff goes here
};
extern template ComplicatedTemplate<std::vector<int>>;
extern template ComplicatedTemplate<std::vector<char>>;
extern template ComplicatedTemplate<std::vector<std::string>>;
Then, in complicated.cpp
:
#include "complicated_template.H"
template ComplicatedTemplate<std::vector<int>>;
template ComplicatedTemplate<std::vector<char>>;
template ComplicatedTemplate<std::vector<std::string>>;
Ok, so that's going to work fine, except for one inconvenience. If you decide to also add ComplicatedTemplate<std::vector<SomeCustomType>>
, or anything else, to the list of pre-instantiated templates, this needs to be done in two places; both in the header file, and in complicated.cpp
Here's a typical approach that eliminates this duplication:
complicated_template.H
:
template<typename T> class ComplicatedTemplate {
// ... Boring stuff goes here
};
#include "complicated_template_inst.H"
complicated_template_inst.H
:
#ifndef EXTERN
#define EXTERN
#endif
EXTERN template ComplicatedTemplate<std::vector<int>>;
EXTERN template ComplicatedTemplate<std::vector<char>>;
EXTERN template ComplicatedTemplate<std::vector<std::string>>;
Then, in complicated.cpp
:
#include "complicated_template.H"
#define EXTERN
#include "complicated_template_inst.H"
Now, the list of template instances that get pre-instantiated is in one place. Using the earlier example, adding:
EXTERN template ComplicatedTemplate<std::vector<SomeCustomType>>;
has the effect of both preventing the wasted instantiation of this template in every translation unit that needs that template instance, and of instantiating it explicitly in the complicated.cpp
translation unit.
You will see this kind of an approach in many large C++ libraries. They will typically define their templates, then pre-instantiating them by pulling in a separate #include file, that contains some preprocessor-fu. The actual shared library will also include the second file a second time, after pulling in the externally-visible header file, with the preprocessor accordingly rigged up to turn those extern template declarations into template instantiations.