Template-Klasse: objektdatei + linker -> Fehler! ohne template gehts
-
David_pb schrieb:
Bei Templates muss, bei den meisten Compilern, die Definition und Deklaration von der gleichen Compiliereinheit sichtbar sein. Meist werden Deklaration und Definition einfach in eine Datei gepackt (vorzugsweise Header ;)). Der Standard gibt zwar die Möglichkeit Templates zu exportieren, allerdings bieten das viele Compiler (die meisten) noch nicht an.
Ok, alles klar. Damit wäre mir geholfen... Danke!
schue schrieb:
Hoi.
klasse meine_klasse<int,int>;muss heissen:
klasse<int,int> meine_klasse;Außerdem hast du in der Template-Klasse das public vergessen:
template <class T1, class T2> class klasse { public: // fehlt im Beispiel void test_func(); };Hmm... Flüchtigkeitsfehler. Hab den Eintrag schnell vor der Schule geschrieben.
Ich danke euch beiden!
Mit der Antwort von David_pb wäre ich soweit bedient (woher weiß man sowas?!)Vielen Danek für die schnelle Antwort.
MfG Knickedi
-
-
Knickedi schrieb:
[(woher weiß man sowas?!)
Sowas bekommt man im Laufe seiner Programmierzeit mit. Du weißt es jetzt ja auch!

-
Braunstein schrieb:
Knickedi schrieb:
...(woher weiß man sowas?!)...
Das steht z.Bsp. in der hiesigen FAQ
Oder man überlegt es sich. Ein Funktionstemplate ist eben etwas völlig anderes als eine Templatefunktion. Traditionell und dem Konzept nach ist das Übersetzungsmodell so ausgelegt, dass Compiler bereits fertigen maschinenlesbaren Code erzeugt und der Linker lediglich noch externe Referenzen auflöst (die üblichweise den Code nur insoweit verändern, als dass noch ein paar Adressen 'eingepatched' werden müssen, was im Prinzip ein recht trivialer Vorgang ist). Für ein Funktionstemplate geht das aber schon im ersten Schritt schief - man kann ein Template nicht in maschinenlesbaren Code transformieren, das geht erst, wenn wir zusätzlich alle Argumente für das template kennen, und dann eine Templatefunktion haben. Diese Templatefunktion dagegen genügt dann wieder allen obigen Anforderungen und ist an sich eine ganz normale Funktion. Aber natürlich kann Compiler nicht spekulativ für alle möglichen Argumente im voraus spezialisieren und Code erzeugen - denn es gibt ja in der Regel sehr viele oder unendlich viele mögliche Argumente. Damit ist schon klar, dass obiger Code jedenfalls nach diesem einfachen Übersetzungsmodell nicht funktionieren kann. Der Sprachstandard sieht hier einmal das Schlüsselwort export vor. Das ist aber einerseits kaum implementiert und auch nicht ohne Probleme, andererseits ist auch klar, dass das, was da (in die Objektdatei) exportiert wird, doch etwas völlig anderes ist als gewöhnliche Funktionen. Andere Abhilfen ergeben sich aber auch zwingend logisch:
- die Notwendigkeit, in main.cpp eine externe (also auf eine andere Übersetzungseinheit verweisende) Referenz zu benutzen, ergibt sich ja nur daraus, dass der Compiler in dieser Übersetzungseinheit nicht in der Lage ist, das Template zu spezialisieren. Und zwar einfach deshalb, weil er dort die Definition des Templates nicht kennt. Beseitigen wir letzteres entfällt auch ersteres (wie in David_pbs Beitrag)
- die andere Möglichkeit setzt am Punkt des "spekulativen" Spezialisierens an. Der Compiler kann nicht wissen, welche Spezialisierungen in anderen Übersetzungseinheiten gebraucht werden und er kann hier auch keine Vorkehr treffen. Der Programmierer hat dieses Problem nicht: Durch explizite (oder implizite) Instanziierung in einer Übersetzungseinheit die die Templatedefinition kennt, werden diese Spezialisierungen (da es sich um normale Funktionen handelt) für alle Übersetzungseinheiten unmittelbar nutzbar. eintemplate klasse<int,int>;in klasse.cpp würde das Problem auch lösen.
Man hat also prinzipiell 3 Möglichkeiten, das Problem zu lösen, wobei die Variante mit export - vor allem mangels Compilerunterstützung - keine praktische Bedeutung hat. Unter den anderen beiden Möglichkeiten wird man dort, wo es möglich ist, in der Regel die wählen, die die Templatedefinition überall sichtbar macht. Zwar wird mit beiden Verfahren das Prinzip der Kapselung im Grunde durchbrochen, der zweite Fall führt aber doch zu deutlich größeren organisatorischen Abhängigkeiten.
-
Danke für den sehr ausführlichen Beitrag.
Wirklich mit Leib und Seele dabei...Also um das kurz und knapp zu fassen (damit ich das auch wirklich richtig verstehe):
1. export vor die Deklaration im Header. Wird aber nicht von jedem Compiler unterstützt (wie ich auch gemerkt habe).
2. "Alles" in einen Header packen und diesen inkludieren. Dann wird die Klasse aber jedes mal mitcompiliert.
3. Der Programmierer spezialisiert sich in der cpp-Datei auf einen Typ des Templates, aber so kann er auch dann nur diesen verwenden.Ich hoffe, dass es richtig angekommen ist...
Aber jetzt stellt sich mir eine Frage:
Wenn ich keine Möglichkeit habe die Klasse als Template in einer Objektdatei auszulagern ohne mich zu spezialisieren oder alles immer neuzucompilieren, wie kriegt der Compiler das mit der STL hin?
Schaff ich das auch? Oder sollte ich die Finger davon lassen?
Nicht dass ich daran scheitere, weil ich die Klasse nicht getrennt compilieren kann, es ist reine Neugier...MfG Knickedi
-
Knickedi schrieb:
1. export vor die Deklaration im Header. Wird aber nicht von jedem Compiler unterstützt
Eigentlich von so gut wie keinem. AFAIK unterstützt zB der Comeau export.
Knickedi schrieb:
2. "Alles" in einen Header packen und diesen inkludieren. Dann wird die Klasse aber jedes mal mitcompiliert.
Eigentlich übersetzt ein Compiler nur das, was auch wirklich instanziert wird. Es ist also nicht so, dass immer sämtliche Templates neu kompiliert werden. Wäre auch ziemlich ineffizient.
Knickedi schrieb:
Wenn ich keine Möglichkeit habe die Klasse als Template in einer Objektdatei auszulagern ohne mich zu spezialisieren oder alles immer neuzucompilieren, wie kriegt der Compiler das mit der STL hin?
Die Implementation der STL befindet sich doch auch grösstenteils in Headern.
-
groovemaster schrieb:
Die Implementation der STL befindet sich doch auch grösstenteils in Headern.
Ich hatte mal irgendwo aufgeschnappt, dass in den Headern "nur" die Deklarationen stehen und Bibliotheken automatisch aufgrund von inkludiereten Dateien aus dem Library-Verzeichnis des Compilers dazugelinkt werden...
Naja, will hier auch keinem weiter zur Last fallen und vom Thema abweichen.
Meine Frage wurde ja eigentlich schon beantwortet.Danke nochmal!
-
Knickedi schrieb:
Ich hatte mal irgendwo aufgeschnappt, dass in den Headern "nur" die Deklarationen stehen
Was ja auch richtig ist. Aber aufgrund des fehlenden Supports von export bilden Templates hier eine Ausnahme.
-
camper schrieb:
...Ein Funktionstemplate ist eben etwas völlig anderes als eine Templatefunktion....
Ist diese Nomenklatur eigentlich verbreitet/standardisiert ?
Ich erinnere mich nur daran, dass man "Funktions-/Klassentemplate" sagen sollte fürtemplate <typename T> void f(void); template <typename T> class C;und nicht "Templatefunktion/-klasse" (um eben klarzustellen, dass es eine Schablone ist).
Das, was der Compiler daraus macht, kenne ich nur als "Instanz" eines (Funktions-/Klassen-)Templates.
Mir fällt allerdings gerade nicht ein, wo ich das gelesen haben (Sutter ? Vandevoorde/Josuttis ? ...)Gruß,
Simon2.
-
Simon2 schrieb:
camper schrieb:
...Ein Funktionstemplate ist eben etwas völlig anderes als eine Templatefunktion....
Ist diese Nomenklatur eigentlich verbreitet/standardisiert ?
Ich erinnere mich nur daran, dass man "Funktions-/Klassentemplate" sagen sollte fürtemplate <typename T> void f(void); template <typename T> class C;und nicht "Templatefunktion/-klasse" (um eben klarzustellen, dass es eine Schablone ist).
Das, was der Compiler daraus macht, kenne ich nur als "Instanz" eines (Funktions-/Klassen-)Templates.
Mir fällt allerdings gerade nicht ein, wo ich das gelesen haben (Sutter ? Vandevoorde/Josuttis ? ...)Gruß,
Simon2.
Man könnte sagen, dass sie standardisiert ist, weil sie bereits in der Sprachdefinition angelegt ist. Die Verwirrung entsteht für den Anfänger vor allem dadurch, dass man (gerade am Anfang in einfachen Fällen ohne Spezialisierung) häufig einfach ein Funktionstemplate definiert, und anschließend ganz normale Funktionsaufrufe (ohne explizite Angabe der Argumente) durchführt. So entsteht natürlich leicht der Eindruck, dass das, was definiert wurde, bereits eine Funktion ist. Denn der Vorgang der Deduktion der Templateargumente und die Instantiierung für diese Argumente ist ja völlig automatisch und implizit. Außerdem gibt es ja beide Begriffe, die des Funktionstemplates als auch der Templatefunktion, die zudem in einem Zusammenhang stehen und daher auch häufig gemeinsam in einem Text auftauchen. Letzteres Problem betrifft dann auch Klassentemplates bzw. Templateklassen (als die konkreten Instanzen eines Klassentemplates). Im Grunde ist es ganz einfach: ein Template ist etwas, das noch durch Parameter näher bestimmt werden muss, und wir nennen es entweder Funktions- oder Klassentemplate, je nachdem, was durch diese Vorlage generiert werden soll, während eine Klasse bzw. Funktion eben gerade keine solchen (Template-)Parameter hat, und wir nennen das dann eine Templateklasse bzw. -funktion, um anzuzeigen, dass dieses Etwas auf ein Template zurückgeht. Wichtig wird diese Unterscheidung noch bei expliziten Templatespezialisierungen. Eine explizite Templatespezialisierung ist kein Template. Das hat wesentliche Auswirkungen auf die ODR. Ein Funktionstemplate muss (nach dieser Lösungsmöglichkeit aus den vorhergehenden Beiträgen) in jeder Übersetzungseinheit definiert werden, in der es gebraucht wird. Eine explizite Spezialisierung dieses Templates ist dagegen eine Funktion und hat damit linkage und unterliegt folglich der ODR (also nur eine Definition im Programm bei extern, es sei denn, wir machen sie inline).