There is no nice way to do it in C++, but there is a hacky way. This is not recommended in any way, unless the alternative is very high.
The idea is to use C++-11 variadic macros (usually a bad idea):
#define DECLARE_AND_ALIAS(Alias, TemplateStart, ...) \
extern template class TemplateStart __VA_OPT__(,) __VA_ARGS__; \
using Alias = TemplateStart __VA_OPT__(,) __VA_ARGS__
These define the two things you need in the header file. Now your example becomes:
template class std::vector<std::string>;//in source file
DECLARE_AND_ALIAS(My, std::vector<std::string>); //in header file
You still need to repeat the same template twice, which you told you'd like to avoid. In that case, it is possible to resort to a greater trickery. You can make your header file behave differently depending on who includes the header file, just like the way dllexport
and dllimport
are handled
#ifdef INSTANTIATE_TEMPLATE
# define DECLARE_AND_ALIAS(Alias, TemplateStart, ...) \
template class TemplateStart __VA_OPT__(,) __VA_ARGS__; \
using Alias = TemplateStart __VA_OPT__(,) __VA_ARGS__
#else
# define DECLARE_AND_ALIAS(Alias, TemplateStart, ...) \
extern template class TemplateStart __VA_OPT__(,) __VA_ARGS__; \
using Alias = TemplateStart __VA_OPT__(,) __VA_ARGS__
#endif
Now, this is imperfect since you don't control for which templates to generate code and which to avoid. For a fine-grained solution things get messy. You can pass a third parameter, that will be responsible for the use of the extern
keyword:
#define DECLARE_AND_ALIAS(Extern, Alias, TemplateStart, ...) \
Extern template class TemplateStart __VA_OPT__(,) __VA_ARGS__; \
using Alias = TemplateStart __VA_OPT__(,) __VA_ARGS__
And the usage
#define MyExtern // compiling My.cpp
#define My2Extern extern // not compiling My2.cpp
DECLARE_AND_ALIAS(MyExtern, My, std::vector<std::string>);
DECLARE_AND_ALIAS(My2Extern, My2, std::map<int, std::string>);
This only barely matches the requirements of the questions, but it does use the variadic macro parameters of C++11. This is not a clean solution, and probably not what you hoped for.
When modules are introduced (hopefully C++20, but possibly only in C++23) this whole extern
trickery will go away, since the import keyword will do the right thing with respect to code generation of templates.