Forum: PC-Programmierung Linker Error bei Klassentemplate


von Frank E. (erdi-soft)


Lesenswert?

Hi Leute,

ich hab hier ein Problem, mit dem ich nicht zurande komme.
Muss als Übung ein Klassentemplate für ein Array schreiben. Aus irgend
einem Grund kann ich es zwar kompilieren, aber das Linken bricht
hiermit ab:

obj\Release\main.o:main.cpp:(.text+0x1f): undefined reference to
`Array<int, 10>::Array()'

main.cpp
1
#include <iostream>
2
#include "array.hpp"
3
4
using namespace std;
5
6
int main(int argc, char *argv[])
7
{
8
   int a;
9
   Array<int, 10> testmich;
10
   return 0;
11
}

array.hpp
1
#ifndef _ARRAY_
2
#define _ARRAY_
3
4
template <class T = char, int size = 256>
5
class Array
6
{
7
   private:
8
      T* pcontent;
9
      int isize;
10
   public:
11
      Array();
12
      ~Array();
13
      T& operator[] (int index);
14
};
15
16
#endif               //_ARRAY_

array.cpp:
1
#include "array.hpp"
2
3
template <class T, int size>
4
Array<T, size>::Array()
5
{
6
   pcontent = new T[this->isize = size];
7
}

hab hier nur mal den Konstruktor angegeben, es macht aber keinen
Unterschied, ob die anderen Methoden mit implementiert sind oder
nicht.

Compiler hab ich sowohl den GCC als auch den Compiler vom Visual Studio
2005 getestet. Immer der gleiche Fehler.

Hat jemand eine Idee?


Gruß, ERDI - Soft.

von Rolf Magnus (Gast)


Lesenswert?

Du hast nicht bedacht, daß die Members eines Klassentemplates in der
Übersetzungseinheit definiert sein müssen, in der das Template
instanziiert wird, da der Compiler ja sonst gar nicht weiß, was er für
einen Code generieren soll. Es gibt zwar eine Möglichkeit unter
Verwendung des Schlüsselwortes "export", aber nur sehr wenige
Compiler unterstützen das, weil es recht problematisch ist.

Eine einfache Lösung ist, am Ende des Headers ein #include <array.cpp>
zu machen und diese Datei dann nicht zu linken.

von Thomas (Gast)


Lesenswert?

Aus dem von Rolf genannten Grund baut man für Templateklassen
normalerweise keine *.ccp-Dateien, sondern schreibt die Definitionen
direkt mit ins Headerfile rein. Das ist ja auch die Crux mit Templates,
massenhaft Templates (z. B. die Boost-Bibliothek) nerven durch lange
Übersetzungszeiten, auch wenn man an den Template-Klassen gar nicht
rumspielt - die sind halt einfach nicht kompiliert.

Unportable Lösungen, die dann nur von exotischen Compilern unterstützt
werden, würde ich nicht verwenden.

von Frank E. (erdi-soft)


Lesenswert?

Danke euch.
Dann werde ich wohl die Definitionen in das Header-File mit reinpacken,
auch wenn mir das nicht wirklich gefällt.

Trotzdem ist mir eines noch nicht ganz klar.
In meinem Fall weiß der Compiler doch, was er für einen Code erzeugen
muss. Und zwar eine Übersetzung des Codes mit int.
Das ist doch, solange man keine Bibliothek baut, eigentlich immer
bekannt.
Und was macht es dann für einen Unterschied, ob die Definitionen nun im
Header stehen oder im .cpp?
Manchmal würde mich der Gedankengang der Compilerbauer schon
interessieren. :-)

von Thomas (Gast)


Lesenswert?

Array.cpp und Array.hpp können zusammen nicht zu einem binären Modul
kompiliert werden, eben weil der Typ nicht festliegt. Deswegen kann es
erst kompiliert werden, wenn Array <T> mit einem konkreten Typ
instanziert wird. Wenn du aber Array.cpp und Array.hpp im
Projekt/Makefile eingebunden hast, wird der Compiler versuchen erstmal
ein Array.obj (Endungen systemabhängig freibleibend ;-)) zu bauen und
das gegen deine main.cpp zu linken. Das schlägt fehl, mangels konkreten
Typs.

Alternative Lösung, wie ebenfalls von Rolf schon beschrieben, wäre es
die Array.cpp und der Array.hpp zu inkludieren (am Schluss eben) und
aus dem Makefile rauszunehmen. Mir persönlich gefällt dies allerdings
nicht, da sich ein cpp/hpp Tuple in meiner Welt immer zu einem
Binärfile kompilieren lassen sollte. Deswegen normalerweise Definition
während Deklaration für Templateklassen und das dann alles im Header.

von Rolf Magnus (Gast)


Lesenswert?

> Unportable Lösungen, die dann nur von exotischen Compilern
> unterstützt werden, würde ich nicht verwenden.

Exotisch würde ich das eher nicht nennen. export ist ein
Standard-C++-Feature, das eigentlich jeder Compiler können sollte.
Unportabel kann man es aber durchaus nennen, weil die Realität etwas
anders aussieht.

> Mir persönlich gefällt dies allerdings nicht, da sich ein cpp/hpp
> Tuple in meiner Welt immer zu einem Binärfile kompilieren lassen
> sollte.

Mir gefällt sie schon, da ich dann wie bei nicht-Templates die übliche
Trennung Interface/Implementation habe. Manche geben in diesem Fall dem
Implementationsfile die Endung .tcc, um zu zeigen, daß es kein
"normales" .cpp-File ist, sondern eine Template-Implementation. Ich
finde das schöner, als alle Implementationen ins Header-File zu
schreiben.

von Rolf Magnus (Gast)


Lesenswert?

Noch mal zur Erklärung:

> In meinem Fall weiß der Compiler doch, was er für einen Code
> erzeugen muss. Und zwar eine Übersetzung des Codes mit int.
> Das ist doch, solange man keine Bibliothek baut, eigentlich immer
> bekannt.

Das ist es eben nicht. Du mußt dir vorstellen, daß der Compiler dein
Programm in Form von Übersetzungseinheiten ("translation units")
baut. Eine Übersetzunseinheit ist vereinfacht gesagt das, was aus dem
Präprozessor rauskommt, also eine .cpp-Datei mit allen #includeten
Headern. Für jede Übersetzungseinheit wird der Compiler einmal
aufgerufen, um sie zu übersetzen. Dabei weiß der Compiler absolut
nichts über die anderen Übersetzungseinheiten.
Ein Template ist noch kein Typ, sondern nur ein Rezept, das dem
Compiler sagt, wie er einen Typ erzeugen muß. Dazu braucht er aber den
Quellcode. Wenn du main.cpp durch den Compiler schickst, weiß dieser
nicht, wie er den Konstruktor von Array<int, 10> erzeugen soll, da er
an der Stelle dessen Quellcode nicht sieht. Dieser ist ja nur in
array.cpp bekannt. Er macht dann einfach weiter in der Hoffnung, daß
der Linker später eine Implementation findet. Der kann aber keine
finden, da sie bisher ja nirgends gebraucht wurde. Deshalb bricht er
mit dem von dir gezeigten Fehler ab.

von Frank E. (erdi-soft)


Lesenswert?

So langsam wirds klar, danke.

Hätte ich eigentlich auch selbst drauf kommen können. Hab ja schon oft
genug den Compile-Vorgang beobachtet und jedesmal auch gesehen, wie er
schön aus jedem .c oder .cpp ein Objekt-File gemacht hat.

Aber manchmal muss man wirklich eines mit der Latte überbekommen, um es
zu kapieren.

Das mit .tpp gefällt mir gut, das werde ich mir merken und anwenden.
Mal gucken, was der Prof dazu sagt. ;-)

von Thomas (Gast)


Lesenswert?

"Export" ist an sich natürlich nicht exotisch ;-) Seine Verwendung mit
Templates allerdings schon.

Was die Trennung von Interface und Implementierung angeht: Das ist
allerdings wahr. C++ Programmierer werden das jetzt nicht gerne hören,
aber zwei manuell zu unterhaltene Dateien für ein Klasse sind sowieso
"suboptimal". Das es anders geht, zeigen andere Sprachen. Braucht man
zur allgemeinen Übersicht nur das Interface so ist es allemal
sinnvoller, dieses automatisch aus dem Code zu generieren.

von Rolf Magnus (Gast)


Lesenswert?

> "Export" ist an sich natürlich nicht exotisch ;-) Seine Verwendung
> mit Templates allerdings schon.

Das ist die einzige Verwendung, die es dafür gibt. Verwechselst du es
vielleicht mit "extern"?

von Thomas (Gast)


Lesenswert?

Du hast allerdings recht, wohl zulange mit "nicht-c++"-Sprachen
gearbeitet ;-)

Aber wie ich sehe, ist "export" ja extrem exotisch und wird nicht von
allen Compilern unterstützt. Dann ist es keine Lösung.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.