C++ Probleme mit den Templates, code funktioniert nicht

konnichi

Lt. Junior Grade
Registriert
Aug. 2009
Beiträge
408
Hallo CB Community.
Mein C++ code läuft nicht. Ich versuche momentan das Thema "Templates" zu verstehen, und habe mir paar Übungsaufgaben ausgedacht. Nun, nach unzähligen Versuchen, Googlen, lesen, Testen gebe ich auf. Ich komme nicht mehr weiter.
Der Code ist ist in 3 Dateien aufgeteilt:
Quelle.cpp
Code:
#include <iostream>
#include "tempplate_Header.h"
using namespace std;



int main()
{

	finden <int> objekt(17, 12);
	cout << objekt.bigger();
	cout << objekt.smaler();


	

	system("Pause");
}

Quelle1.cpp
Code:
#include <iostream>
#include "tempplate_Header.h"
using namespace std;

template <class G>
finden<G>::finden(G a,G b)
{
	G zahl1;
	G zahl2;
	zahl1 = a;
	zahl2 = b;

}


template <class G>
G finden<G>::bigger()
{
	return (zahl1 > zahl2 ? zahl1 : zahl2);
}

template <class G>
G finden<G>::smaler()
{
	return (zahl1 < zahl2 ? zahl1 : zahl2);
}

..und die tempplate_Header.h Datei:

Code:
#include <iostream>
using namespace std;

template <class G>
class finden
{
public:
	finden(G a, G b);
	G bigger();
	G smaler();
};

Also, es gibt folgenden Erorr:
Fehler 1 error LNK2019: Verweis auf nicht aufgelöstes externes Symbol ""public: __thiscall finden<int>::finden<int>(int,int)" (??0?$finden@H@@QAE@HH@Z)" in Funktion "_main"
Fehler 2 error LNK2019: Verweis auf nicht aufgelöstes externes Symbol ""public: int __thiscall finden<int>::bigger(void)" (?bigger@?$finden@H@@QAEHXZ)" in Funktion "_main".
Fehler 3 error LNK2019: Verweis auf nicht aufgelöstes externes Symbol ""public: int __thiscall finden<int>::smaler(void)" (?smaler@?$finden@H@@QAEHXZ)" in Funktion "_main".
Fehler 4 error LNK1120: 3 nicht aufgelöste Externe

Woran liegt das Problem? Was habe ich falsch gemacht, bzw. falsch deklariert?
Ich habe gelesen, dass man alle Templates in der main.cpp packen kann, und es würde keine Probleme geben. Das will ich aber nicht. Es muss so sein wie bei den "Profis" :D alles schön aufgeteilt wie sich das gehört. Ordnung muss sein.
Könnte mir jemand irgendein Tipp geben um das Problem zu lösen?
Besten Dank im Voraus :)
 
Zuletzt bearbeitet:
Die Definition des Templates, also der Code, der in deiner Quelle1.cpp steht, muss in den Header.

Das Problem ist, zu verstehen, wie Templates in C++ instantiiert werden. Falls du aus der Java-Ecke kommen solltest, wo Generics irgendwie als ganz normaler Code compiliert werden, wo der Typ intern einfach durch Object ersetzt wird: So funktioniert das hier nicht.

Ein Template musst du dir einfach als abstrakte Definition einer Klasse oder Funktion vorstellen. Wenn der Compiler ein Template findet, ist ihm erst einmal relativ egal, was da drin steht - wichtig ist erst einmal nur: Okay, hier ist ein (Klassen- oder Funktions-)Template mit dem und dem Namen und den und den Template-Parametern. Das ändert sich auch erst, wenn das Template instantiiert wird.

Das passiert in deinem Fall in dieser Zeile:
Code:
finden<int> objekt(17, 12);
Hier wird finden erstmalig benutzt, eben mit dem Template-Parameter int.

Erst an dieser Stelle wird überhaupt Code generiert, und zwar spezifisch für den Typen int. Damit das passieren kann, muss der Compiler aber wissen, wie der Code für die Klasse überhaupt aussieht, und das kann er nur, wenn der Code auch im Header mit drin steht:

Code:
template <class G>
class finden {
public:
  finden(G a, G b) {
    // ...
  }
  G bigger() {
    // ...
  }
  G smaler() {
    // ...
  }
};

Anders herum: Wenn du deine aktuelle Quelle1.cpp compilierst, wird sie gar keinen Code enthalten, weil das Template nie mit einem konkreten Typen benutzt wird.
 
Code:
template <class G>
finden<G>::finden(G a,G b)
{
	G zahl1;
	G zahl2;
	zahl1 = a;
	zahl2 = b;
 
}

Was sollen die sinnlosen Zuweisungen im Konstruktor? Du belegst hier 2 lokale Variablen mit den Werten der übergebenen Argumente und verwendest sie dann nicht mehr. Sollten zahl1 und zalh2 nicht viel eher Membervariablen der Klasse sein?
 
hallo,
die Grundidee war, dass ein Konstruktor für jeden erstellten Objekt 2 Variablen hinterlegt, auf die ich dann intern zugreifen kann. Deswegen:
Code:
template <class G>
finden<G>::finden(G a,G b)
{
	G zahl1;
	G zahl2;
	zahl1 = a;
	zahl2 = b;
 
}
..aber so wie das aussieht, funktioniert das bei Templates nicht. Kann man dann dem Konstruktor keine Aufgabe erteilen??
Jetzt nach viel hin und her, endlich hat es geklappt. Wie empfohlen geht auch die Definition der Funktionen in den Header.
Code:
#include <iostream>
using namespace std;

template <class G>
class finden
{
public:
	finden()
	{};
	G bigger(G a, G b)
	{
		return (a > b ? a : b);

	};
	G smaler(G a, G b)
	{

		return (a < b ? a : b);
	};
};

Ich sehe ein Nachteil darin, dass die Datei Quelle1.cpp ihre Existenzrechtfertigung verliert. Plus sind meine ganze Methoden in der Headerdatei sichbar. Also die eigentliche Arbeit. Aber ich nehme an, dass es mit den Templates nicht anders geht.
Danke sehr für die Denkanstöße. Ich kann heute ruhig schlafen deswegen :D
 
Plus sind meine ganze Methoden in der Headerdatei sichbar. Also die eigentliche Arbeit.
Wo ist das Problem?

Das einzige, was wirklich etwas nervig ist, ist, dass Code mit vielen Templates ſehr lange zum Compilieren braucht. Auf nen kompletten Rebuild von einem Projekt mit ~90k Zeilen eigenem Code und sehr vielen Templates warte ich inzwischen über 50 Sekunden, dagegen ist AsmJit mit ca. 55k Zeilen ohne Templates in unter 5 Sekunden durch.

..aber so wie das aussieht, funktioniert das bei Templates nicht. Kann man dann dem Konstruktor keine Aufgabe erteilen??
Doch, kannst du. Machst du ja offensichtlich auch, nur dass die Arbeit in deinem Fall nicht sinnvoll ist.
Warum tut sie nichts? Du erstellst zwei lokale Variablen, weist ihnen einen Wert zu und löschst sie dann sofort wieder, ohne, dass damit irgendetwas passiert.

Was du willst, ist der Klasse private Member-Variablen geben:
Code:
template<class G>
class finden {
public:
  finden(const G& a, const G& b)
  : zahl1(a), zahl2(b) { }
  [...]
private:
  G zahl1;
  G zahl2;
};
 
Zurück
Oben