Нет хорошего способа сделать это в C ++, но есть хакерский способ.Это никоим образом не рекомендуется, если только альтернатива не очень высока.
Идея состоит в том, чтобы использовать макросы C ++ - 11 (обычно это плохая идея):
#define DECLARE_AND_ALIAS(Alias, TemplateStart, ...) \
extern template class TemplateStart __VA_OPT__(,) __VA_ARGS__; \
using Alias = TemplateStart __VA_OPT__(,) __VA_ARGS__
Они определяютдве вещи, которые вам нужны в заголовочном файле.Теперь ваш пример выглядит так:
template class std::vector<std::string>;//in source file
DECLARE_AND_ALIAS(My, std::vector<std::string>); //in header file
Вам все еще нужно повторить один и тот же шаблон дважды, чего, как вы сказали, вы хотели бы избежать.В этом случае можно прибегнуть к еще большему обману.Вы можете заставить свой файл заголовка вести себя по-разному в зависимости от того, кто включает файл заголовка, точно так же, как обрабатываются dllexport
и dllimport
#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
Теперь это несовершенно, так какВы не контролируете, для каких шаблонов генерировать код, а какие избегать.Для мелкозернистого решения вещи становятся грязными.Вы можете передать третий параметр, который будет отвечать за использование ключевого слова extern
:
#define DECLARE_AND_ALIAS(Extern, Alias, TemplateStart, ...) \
Extern template class TemplateStart __VA_OPT__(,) __VA_ARGS__; \
using Alias = TemplateStart __VA_OPT__(,) __VA_ARGS__
И использование
#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>);
Это только едва соответствует требованиямвопросы, но он использует переменные макропараметры C ++ 11.Это не чистое решение и, вероятно, не то, на что вы рассчитывали.
Когда будут введены модули (возможно, C ++ 20, но, возможно, только в C ++ 23), весь этот extern
трюк исчезнет., поскольку ключевое слово import будет работать правильно в отношении генерации кода шаблонов.