Нет, это не то же самое, что обычная функция. С помощью обычной функции вы можете объявить
void foo(int);
void foo(double);
в заголовке, определите функции в некотором исходном файле, например, foo.cc
, #include заголовок в любом исходном файле, который должен использовать эти функции, например, bar.cc
, и пусть компоновщик сделает все остальное. Компилятор скомпилирует bar.cc
и выдаст bar.o
, уверенный, что вы где-то определили функции, а если нет, вы получите ошибку во время соединения.
Но если вы используете шаблон:
template <typename T>
void foo(T) ...
попытайтесь представить, как это будет работать. Исходные файлы foo.cc
и bar.cc
независимы и ничего не знают друг о друге, за исключением того, что они согласны с тем, что в заголовках они оба #include (вот и вся идея). Так что bar.cc
не знает, как foo.cc
реализует вещи, а foo.cc
не знает, что bar.cc
будет делать с этими функциями. В этом сценарии foo.cc
не знает , какой тип bar.cc
будет указывать для T . Так как же foo.cc
может иметь определения для каждого typename под солнцем?
Не может, поэтому такой подход не разрешен. У вас должен быть весь шаблон в заголовке, чтобы компилятор мог сгенерировать определение для foo(int)
, или foo(string)
, или foo(myWeirdClass)
, или чего-либо, что bar.cc
требует, и встроить его в bar.o
( или пожаловаться, если шаблон не имеет смысла для этого типа).
То же самое касается классов.
Правила немного отличаются для шаблонных специализаций, но вы должны хорошо разбираться в основах, прежде чем пытаться использовать продвинутые методы.