Шаблоны - это, по сути, полутип-безопасные макросы, отсюда и ограничение.
Обычные (не шаблонные) функции могут быть скомпилированы с собственным кодом, находящимся в объектных / библиотечных файлах, и затем на них можно ссылаться только с помощью прототипа, доступного в заголовке, только потому, что существует только одна версия такой функции.
При использовании шаблонов компилятор C ++ должен компилировать каждое создание функции отдельно. Очевидно, что он не может сделать это «заранее», потому что набор типов, для которых вы можете создать экземпляр функции, фактически неограничен (вы всегда можете определить новый тип в вашем коде перед вызовом функции). Фактически, два экземпляра одного и того же шаблона функции могут быть совершенно разными. Рассмотрим этот крайний случай:
struct t1 {
template <int>
struct a {};
};
struct t2 {
enum { a = 123 };
};
enum { b = 456, c = 789 };
template <class T>
void foo() {
T::a<b>c;
}
Теперь, если мы вызываем foo<t1>()
, оператор внутри него является объявлением локальной переменной, потому что t1::a
является шаблоном класса:
T::a<b> c;
Но если мы вызываем foo<t2>()
, оператор внутри является выражением, потому что t2::a
является интегральной константой:
(T::a < b) > c;
Это просто для того, чтобы показать, что компилятор не может осмысленно «скомпилировать» шаблон; это действительно должно в основном сохранять токены.
Теперь, все это говорит о том, что ISO C ++ действительно позволяет разделить объявление и определение шаблонов, чтобы их можно было рассматривать как обычные функции с объявлениями в файлах .h и определениями в файлах .cpp. Это называется «шаблоны экспорта», потому что вы должны предшествовать как объявлению, так и определению с ключевым словом export
:
// link.h
export template <class T>
T *Link(T *&, T *(*)());
// link.cpp
export template <class T>
T *Link(T *&ChildNodeReference, T *(*ObjectCreator)()) {
}
Это, однако, противоречивая особенность Стандарта из-за очень высокой нагрузки на реализацию, и большинство популярных реализаций отказываются от ее реализации; в частности, g ++, MSVC и C ++ Builder не реализуют его. Единственный известный мне компилятор, который поддерживает это Comeau C ++ .