Przedmówcy wyjaśnili już jak rozwiązać problem to ja może napisze 2 zdania czemu taki problem występuje i czemu nie ma nic złego w definiowaniu szablonu funkcji w pliku .h (.hpp).
Dla jasności:
- Deklaracja szablonu funkcji:
template <typename T>
void fun(T x);
-
Definicja szablonu funkcji:
template <typename T>
void fun(T x) {}
Podkreślam. Są to szablony funkcji a nie funkcje szablonowe. Te twory same w sobie nie są funkcjami. Są przepisem jak stworzyć funkcje.
Przepis ten jest wykorzystywany do wygenerowania funkcji w dwóch przypadkach:
- niejawnie - gdy ktoś zawoła funkcję
fun(1);
-
jawnie:
template fun(int x);
Żeby kompilator mógł wygenerować funkcję z szablonu - definicja tego szablonu musi być kompilowana razem z kodem generującym instacje funkcji:
template <typename T>
void fun(T x){}
template void fun(double x){}
int main(){
fun(1);
}
Jeśli rozdzielisz deklaracje z definicją to kompilator najpierw sobie skompiluje szablon. A potem gdy będziesz próbował wywołać funkcję poleci błąd linkera, bo wywołanie zgadza sie z deklaracją szablonu, ale funkcja nie została wygnerowana z tego szablonu. Kompilator jest krótkowzroczny. Jak kompiluje jeden plik szablon.cpp to nie widzi jakie funkcje, które mozna wygenerowac z tego szablonu będą później potrzebne.
Co zrobić, jak żyć?
Nie przejmować się tylko wrzucić definicje do pliku h. Czemu tak można (czemu to nie łamie one-definition-rule):
There can be more than one definition in a program, as long as each definition appears in a different translation unit, of each of the following: class type, enumeration type, inline function with external linkage inline variable with external linkage (since C++17), class template, non-static function template, static data member of a class template, member function of a class template, as long as all of the following is true:
...
Trzema kropkami sie generalnie nie przejmujemy. Kilka oczywistych oczywistosci (np, ze definicje musza byc takie same) i wyjątków (np, ze linkujemy z tym samym jezykiem).
Można też tak jak koledzy wspomnieli jawnie wygenerować odpowiednie instacje w pliku szablon.cpp:
template <typename T>
void fun(T x){}
template void fun(int x);
Wtedy definicja może zostać w pliku .cpp, ale trzeba pamiętać, żeby jawnie wygenerowac wszystkie użyte gdzieś indziej instancje (tak, bardzo łatwo o tym zapomnieć, a czasem wręcz się nie da - gdy piszesz biblioteke :P)
EDIT: jako ciekawostka - istnieje różnica pomiędzy:
//A.h
class A {
template <typename T>
void fun(T x);
};
//wciaz A.h
template<typename T>
void A::fun(T x) {}
a
class A {
template <typename T>
void fun(T x) {}
};
W drugim przypadku fun jest niejawnie zdeklarowna jako inline