Template, Veraerbung
-
Genau sowas lies sich bei mir mit dem VC7.1 nicht kompilieren. Da kam der selbe Fehler wie im Builder.
In einem Buch habe ich gelesen das die Aufteilung nicht in der ursprünglichen Natur läge musste dieses Schlüsselwort eingeführt werden um dem Compiler mitzuteilen das sich Template-Definition und Template-Methoden-Definitionen in unterschiedlichen Dateien befinden, dazu ein Beispiel:export template<class Typ, int S> class Feld { blalbla... };
Habe eben aber nochmal in "Die C++-Programmiersprache" geguckt und mich eines besseren belehren lassen. Muss ich nochmal gucken ob ich da falsch gemacht habe bei meinem Template. Mich wundert nämlich das es, wenn ich es nur in der .h habe, funktioniert und getrennt nicht übersetzt wird.
Code-Hacker
-
Code-Hacker schrieb:
Genau sowas lies sich bei mir mit dem VC7.1 nicht kompilieren. Da kam der selbe Fehler wie im Builder.
In einem Buch habe ich gelesen das die Aufteilung nicht in der ursprünglichen Natur läge musste dieses Schlüsselwort eingeführt werden um dem Compiler mitzuteilen das sich Template-Definition und Template-Methoden-Definitionen in unterschiedlichen Dateien befinden, dazu ein Beispiel:export template<class Typ, int S> class Feld { blalbla... };
Habe eben aber nochmal in "Die C++-Programmiersprache" geguckt und mich eines besseren belehren lassen. Muss ich nochmal gucken ob ich da falsch gemacht habe bei meinem Template. Mich wundert nämlich das es, wenn ich es nur in der .h habe, funktioniert und getrennt nicht übersetzt wird.
Code-Hacker
Du hast schon recht mit dem Schluesselwort export.
Andernfalls, muss eine template-Funktion in jeder Implementationsdatei
definiert werden, oder es kann sein, dass der Compiler die Definition einer
solchen Funktion nicht findet und dann meldet sich der Linker.mfg
v R
-
Stimmt. Kompilieren geht, Linken geht nicht.
Code-Hacker
-
falls du das kleine Beispiel aus meinem vorherigen Posting meinst, dann geht beides sonst hätte ich es erwähnt.
-
Also ich habe eben mit VS7.1 dein Template benutzt und das versucht. Funktionierte nicht. Es kam folgender Fehler:
CPPTests error LNK2019: Nicht aufgelöstes externes Symbol '"public: void __thiscall B<int>::foo(void)" (?foo@?$B@H@@QAEXXZ)', verwiesen in Funktion '_main'
So sehen die Dateien aus:
test.cpp:#include "test.h" template<class T> void B<T>::foo() { }
test.h:
template<class T> class B { public: void foo(); };
main.cpp
#include <iostream> #include "test.h" int main() { B<int> n; n.foo(); std::cin.get(); }
Mache ich irgendwas falsch???
EDIT:
Wenn ich test.h wie folgt ändere funktioniert es:template<class T> class B { public: void foo(); }; template<class T> void B<T>::foo() { }
Code-Hacker
-
füge in die main.cpp folgendes ein
#include "test.cpp"
-
Das ist aber übler Stil und hat nichts mit export zu tun. cpps gehören nicht inkludiert.
-
operator void schrieb:
Das ist aber übler Stil und hat nichts mit export zu tun. cpps gehören nicht inkludiert.
Die Endung .cpp ist schlecht gewählt, aber so ein #include ist gängige Praxis.
-
Ich würde als Endung *.inl vorschlagen.
-
Shlo schrieb:
füge in die main.cpp folgendes ein
#include "test.cpp"
Das ist exakt _das_ was CodeHacker und ich sagten und _kein_ Unsinn gewesen!
Wenn du kein export hast, benoetigst du die Implementation der templates in
jeder Implementationsdatei, in welcher du die Templates einsetzt, nicht mehr
und nicht weniger hab ich gesagt.mfg
v R
-
Es gibt noch eine Möglichkeit, die explizite Instantiierung heisst.
//die Zeile gehoert zu der Cpp-Datei template class B<int>;
-
desert pinguin schrieb:
Es gibt noch eine Möglichkeit, die explizite Instantiierung heisst.
//die Zeile gehoert zu der Cpp-Datei template class B<int>;
Schon. Und so kann ich eine Funktion schreiben, die 42 zurückgibt:
int zweiundvierzig () { return 42; }
-
Ach ja, eigentlich schreibt man in solchen Fällen ja einfach ein 99 Bottles of Bear programm als Antwort:
module Main where newtype Bottle = Bot Int instance Show Bottle where show (Bot 0) = "No more bottles" show (Bot 1) = "1 bottle" show (Bot n) = show n ++ " bottles" data Line = Simple String | Complex Bottle String instance Show Line where show (Simple s) = s show (Complex b s) = show b ++ s verse n = [Complex (Bot n) " of beer on the wall,", Complex (Bot n) " of beer.", Simple "Take one down pass it around", Complex (Bot $ n-1) " of beer on the wall.\n"] main = do mapM print $ concatMap verse [99, 98 .. 1]
-
Helium schrieb:
desert pinguin schrieb:
Es gibt noch eine Möglichkeit, die explizite Instantiierung heisst.
//die Zeile gehoert zu der Cpp-Datei template class B<int>;
Schon. Und so kann ich eine Funktion schreiben, die 42 zurückgibt:
int zweiundvierzig () { return 42; }
Ich wollte nur sagen, dass in diesem Fall keine cpp-Datei beinhalten werden muss. Nur die header-datei.
-
Shade Of Mine schrieb:
Die Endung .cpp ist schlecht gewählt, aber so ein #include ist gängige Praxis.
Wenn jemand die Elementfunktionen in eine separate Datei steckt, soll er das von mir aus tun (auch wenn ich keinen Vorteil darin sehe?), aber .cpp und MSVC klingt schon warnenswert. Vor allem, weil es anfangs problemlos funktioniert und erst später zu Fehlern führt, wenn man z.B. eine nicht-template-Hilfsfunktion definiert. (Klar kann man die Kompilierung für einzelne Dateien abschalten, aber das ist auch nicht Sinn der Sache...)