Template Klassen in header und source files
-
Mein Problem ist folgendes:
Ich bekomme folgenden Linker fehler...
main.o(.text+0x141):main.cpp: undefined reference to `klar<int>::klar(int, int)'main.o(.text+0x14c):main.cpp: undefined reference to `klar<int>::getmax()'
collect2: ld returned 1 exit statusWie ich mein Problem beheben kann weiß ich ebenfalls, mit inline Methoden z.B.
Ich möchte aber wissen, wie es mit meiner Schreibweise ohne inline funktioniert.
Also der Konstruktor und die getmax Methode. in der cpp definiert wird, nicht inline in der .h.Hier mal der Source der Files wie es funktionieren soll:
main.cpp
#include "klar.h" #include <iostream> using namespace std; int main() { klar <int> myobject (100, 75); cout << myobject.getmax() << endl; return 0; }
klar.h
#ifndef __KLAR_H #define __KLAR_H template <class T> class klar { T value1, value2; public: klar(T, T); T getmax(); }; #endif
klar.cpp
#include "klar.h" template <class T> klar<T>::klar(T first, T second) { value1=first; value2=second; } template <class T> T klar<T>::getmax() { T retval; retval = value1>value2? value1 : value2; return retval; }
-
Das hatten "wir" hier schon oft.
Du kannst Templates nur mit dem Schlüsselwort export (wird kaum unterstützt) oder durch einen "Hack" aufteilen.
Ich rate dir zu dem Hack.// template.h #pragme once // oder Includeguard template <class T> class Einmalspeicher { public: Einmalspeicher(const T& item_); T getItem() const; private: T item; }; #include "template.cpp"
// template.cpp // Evtl. noch template.h includieren, damit der Kompiler auch weiterkommt. Einmalspeicher::Einmalspeicher(const T& item_) : item(item_) {} T Einmalspeicher::getItem() const{ return item; }
// Edit
Außerdem würde ich dir beim Konstruktor zur Initialisierungsliste raten.
AlsoKlasse(var1, var2, var3 = DEFAULT_WERT) : m_var1(var1) , m_var2(var2) . m_var3(var3) { }
In diesem Beispiel würdest du dir damit drei Zuweisungen sparen, da die Variablen gleich mit dem richtigen Wert erstellt (initialisiert) werden.
-
template <class T> T klar<T>::getmax() { return ( value1>value2? value1 : value2 ); }
So hast du zieeemlich viel gespart
und das nicht nur von den Codezeilen her...
-
Und wenn wir schon im Standard C++ Forum sind benutzen wir daher auch die STL.
template <class T> T klar<T>::getmax() { return std::max(value1, value2); }
-
Selbst mit dem "Hack" scheint es nicht zu funktionieren...
Ich compiliere übrigens auf MinGW Version 3.4.2
Habe auch schon verschiedene Compiler flags ausprobiert, ohne Erfolg.
Im Struppi steht nicht viel zu dem Thema der Aufteilung,
er schneidet nur kurz die Lösung mit extern an, aber nur für Funktionen.
Die Meldung ist mir auch klar, er versucht neu zu definieren aber warum?Hier mal der Compilerfehler:
klar.cpp:5: error: redefinition of `Einmalspeicher<T>::Einmalspeicher(const T&)' klar.cpp:5: error: `Einmalspeicher<T>::Einmalspeicher(const T&)' previously decl ared here klar.cpp:9: error: redefinition of `T Einmalspeicher<T>::getItem() const' klar.cpp:9: error: `T Einmalspeicher<T>::getItem() const' previously declared he re
Hier die Source Dateien:
main.cpp
#include "klar.h" int main() { const int x=1; Einmalspeicher<int> field(x); return 0; }
klar.cpp
#include "klar.h" template<class T> Einmalspeicher<T>::Einmalspeicher(const T& item_) : item(item_) {} template<class T> T Einmalspeicher<T>::getItem() const{ return item; }
klar.h
#ifndef __KLAR_H #define __KLAR_H #pragma once template <class T> class Einmalspeicher { public: Einmalspeicher(const T& item_); T getItem() const; private: T item; }; #include "klar.cpp" #endif
-
das
#include "klar.h"
in klar.cpp muss weg.
Kurt
-
klar.cpp
#include "klar.h" /* das muss weg. diese datei braucht die definition nicht, da sie später von der klar.h includiert wird und somit eine deklaration vorhanden ist. */ template<class T> Einmalspeicher<T>::Einmalspeicher(const T& item_) : item(item_) {} template<class T> T Einmalspeicher<T>::getItem() const{ return item; }
mfg
-
Ok, es klappt nun.
#pragma once kann ich mir auch sparen, habe doch schon den Includeguard.
Musste erstmal verstehen, warum ich die klar.cpp nicht mitkompilieren darf.Danke nochmal an alle!
-
Hallo, Ich hab noch eine Frage dazu.
Wenn ich eine Klasse hab in der eine Template Funktion ist
class TestClass { public: template<class T> void test_funk( const T &test ); };
Gibt´s dann eine Möglichkeit, die TestClass in einer header-datei zu deklarieren und die test_funk in eine C++ Datei zu packen?
-
das wurde doch schon besprochen
-
@`??????????:
In den Beispielen bis jetzt war immer die ganze Klasse eine Template.
Jetzt hab ich eine normale Klasse mit einer Template Funktion.
-
same difference ....
-
same story...
-
Eben nicht.
Es kam ja heraus, dass man die Funktion in der C++ Datei vor dem includieren der Header-Datei schreiben soll.
Das geht ja nicht, weil die Funktion ja Member der Klasse ist, die in der Header-Datei deklariert wurde.
Jetzt klar was ich meine?
-
Gap schrieb:
Eben nicht.
Es kam ja heraus, dass man die Funktion in der C++ Datei vor dem includieren der Header-Datei schreiben soll.
Das geht ja nicht, weil die Funktion ja Member der Klasse ist, die in der Header-Datei deklariert wurde.
Jetzt klar was ich meine?Da hast du was falsch verstanden. Der hack war dass du die implementation in irgendeine datei (warum nicht .cpp) schreiben kannst und diese dann am ende der headerdatei in den header includierst.
BTW würde die datei eher .ipp nennen.
Kurt
-
Oops, dann hilfts mir eh nicht, weil ja genau die Übersicht dadurch verloren geht.
Ich will halt die Klasse in einer Header Datei und alle Funktionen der Klasse in einer C++ Datei haben.
Das geht nicht, oder?
PS:
Ich hab mal in einem Tutorial gelesen, das zu einem sauber Programmier Stil gehört, dass man nie eine C++ Datei includieren soll.
-
you got it.
-
Gap schrieb:
Das geht nicht, oder?
PS:
Ich hab mal in einem Tutorial gelesen, das zu einem sauber Programmier Stil gehört, dass man nie eine C++ Datei includieren soll.Stimmt alles.
Es geht nicht, weil die meisten Compiler (bis auf Comeau) export nicht unterstützen. (Ich muss sogar zugeben, dass ich export nur vom hörensagen kenne, weil ich es beim G++ nie benutzen kann.)Und keine Cpp Dateien einbinden. Dafür gibt es Headerfiles und Linker.
mfg
-
Ich will halt die Klasse in einer Header Datei und alle Funktionen der Klasse in einer C++ Datei haben.
Geh doch mal mit Logic ran und versuch zu begreifen, was da vor sich geht ....
Du sagst in nem header, das du ne klasse / Funktion generisch als template baust. Das heisst, der Compiler kann aus dem template erst dann maschinencode bauen, wenn du das template voll parametrisiert benutzt.
Also wird das template am benutzer (jede cpp datei, die das template direkt oder indirekt einbindet) compiliert und dann dazugelinkt. Normal iss der compiler dann nur noch so schlau, templates mit selben parametern nich doppelt zu generieren, sondern dann nur noch fuer den linker symbole auf das schon in ner anderen cpp datei eingehaengten templates zu generieren.Mit ner eigenstaendigen cpp datei fuer das template wuerde das nich funktionieren ... warum ? weil alle cpp dateien unabhaengig compiliert werden, und mit welchen paramatern soll er das template in diesem falle bauen ?
Also definition und deklaration im falle eines templates zu trennen, iss unnatuerlich, und klar, das das nur mit "schmutzigen tricks" geht ...
Deshalb, templates gehoeren normalerweise in den header ausgepraegt. Wenn du templates baust, die so unuebersichtlich sind, dass du den starken drang hasst, deklaration von der definition zu trennen, solltest dein Design eh noch mal ueberdenken.
Templates sollten kurz und knackig sein, ansonsten blaeht es bei uebermaessiger nutzung deinen erzeugten code nur unnutz auf ....Ciao ...
-
RHBaum schrieb:
Mit ner eigenstaendigen cpp datei fuer das template wuerde das nich funktionieren ... warum ? weil alle cpp dateien unabhaengig compiliert werden, und mit welchen paramatern soll er das template in diesem falle bauen ?
Also definition und deklaration im falle eines templates zu trennen, iss unnatuerlich, und klar, das das nur mit "schmutzigen tricks" geht ...
Nö. Was Du beschreibst ist das Inclusion Model, was auch jeder gewohnt ist und kennt. Nebenbei gibt es auch noch das Separation Model, dass die separate Kompilierung von Templates in eigenen cpp Dateien vorsieht (was man genau mit dem Schlüsselwort export erreicht).
Soweit, so gut. Das sind die Models an sich. Das Separation Model, wie es im Standard beschrieben wird, ist aber unzureichend spezifiziert und führt zu ganz ernsthaften Problemen. Z.B. wird das Template in einer separaten Datei kompiliert. Aber je nach dem muss es -huch- plötzlich Symbole aus anonymen namespaces anderer cpp-Dateien kennen können. Eine absolut widersprüchliche Eigenschaft. In der cpp-Datei des Templates laufen auf einmal auch massig verschiedene includes zusammen, die durch die versteckten Abhängigkeiten entstehen. Und was alles passieren kann, wenn der Code mit Templateparameter T=Klasse A plötzlich die includes von Templateparameter T=Klasse B sieht, kann sich wohl jeder ausmalen (geschweige denn die Kollision von Symbolen aus anonymen namespaces). Deswegen Finger weg vom Separation Model und dem Schlüsselwort export.