Template: Frage zum Konstruktor
-
Ich beschäftige mich gerade mit Templates. Nun hab will ich einen Template-Konstruktor schreiben, der mir zwei Variablen initialisiert:
template<typename T, typename S> class Test { private: T var1; T var2; public: Test(); };template<typename T, typename S> Test<T,S>::Test() : var1(0), var2(0) { }Mit dem obigen Code bekomme ich aber einen Linker Error. Benutzte IDE/Compiler: VS2010. LinkerFehler:
Fehler 1 error LNK2019: Verweis auf nicht aufgelöstes externes Symbol ""public: __thiscall Test<int,int>::Test<int,int>(void)" (??0?$Test@HH@@QAE@XZ)" in Funktion "_main".Fehler 2 error LNK1120: 1 nicht aufgelöste externe Verweise.
Wo ist da nun der Fehler? Diesen LinkerFehler kenn ich eigentlich nur immer davon, wenn ich eine Methode in der h-Datei deklariert habe, diese aber dann nicht implemeniert habe. Das ist hier aber nun nicht der Fall...
Edit: Achja, aufrufen tu ich das ganze dann so:
Test<int, int> A;
-
Lies mal unter häufig gestellte Fragen unter Punkt 5.
Dazu noch http://www.c-plusplus.net/forum/p1993089#1993089 von wxSkip ab Punkt 4.
-
Zitat aus die "meistgestellten Fragen":
Mögliche Workarounds:
- alles direkt in die Klassendefinition einfügen; das wird bei großen Templates ziemlich unübersichtlich
- am Ende der Header-Datei eine Implementationsdatei inkludieren, in der die Member definiert werden (üblicherweise hat diese die Endung .impl)
- die gewünschten Template-Instanzen explizit definieren
- das Schlüsselwort export benutzen; allerdings gibt es kaum Compiler, die dieses unterstützenPunkt 1 will ich nicht, da es meiner Ansicht nach schlechter Stil ist.
Punkt 2 hört sich schon eher nach dem an was ich will. Leider hab ich dazu nirgends was gefunden, wie das mit dieser Implementationsdatei funktioniert. Wie würdest du da vorgehen?
-
das was du von Punkt 1 nicht willst, in die zweite Datei auslagern
greetz KN4CK3R
-
@KN4CK3R: Aber genau das ist ja das Problem... Ich will quasi bis auf die Definition des Templates aus der Klasse alles draußen haben und genau deswegen hat mir ja icarus2 genau den Link mit den Tips geschickt...
Anderes Problem: Ich hab mich jetzt mal dazu hinreissen lassen, die Implementation des Template-Konstruktors in der header-Datei zu machen. Dabei hab ich nun zwei Konstruktion definiert. Einen der über die Konstruktorliste die zwar Variablen der Klasse mit einem Standardwert initialisiert und einen Template-Konstruktor, der mir über von der main übergebene Werte über die Konstruktorliste an den Konstruktor "weiterleitet". Dabei hab ich folgendes festegestellt:
Test<T,S>::Test() : var1(0), var2(0) //Konstruktor 1 { } Test<T,S>::Test(T v1, S v2) : var1(v1), var2(v2) /Konstruktor 2 { }So wie es jetzt da steht meckert der Compiler mit 17 Fehler. Ich kann die Fehler hier jetzt nicht posten. Das sind zu viele. Wenn ich aber nun bei Konstruktor 2 das
Test<T,S>::weglasse, dann kompiliert er ohne zu meckern. Bei Konstruktor 1 macht das Vorhanden- oder nicht Vorhandensein von
Test<T,S>::keinen Unterschied. Dieser wird IMMER kompiliert.
-> Warum ist das so?
Weitere Frage:
Ich hab mir jetzt zur Abwechslung mal eine von Klassen unabhängige Funktion geschrieben. Diese Funktion soll zwei übergebene Variablen miteinander addieren.
template<typename T1, typename T2> T1 add(T1 a, T2 b) { return a+b; }Der return-Wert hängt also vom Aufruf der Funktion add aus der main ab. Wenn ich die Funktion add nun so aufrufe
add(5,5)ist der return-Wert ein int, da der erste Parameter des Funktionsaufrufs ein int ist und in der Funktion als return-Wert der erste Parameter gewählt wurde. Soweit so gut. Wenn ich nun
add(5.5,5)aufrufe, dann bekomm ich welchen Wert zurück? float oder double? Das verstehe ich nun nicht. Es ist klar, wenn ich den return-Wert der template-Funktion in eine double-Variable speichern lasse, dann ist logischerweise der return-Wert ein double. So lange die übergebene Gleitkommazahl kleiner als float ist, ist das ja kein Problem. Wenn ich aber eine "echte" double-Zahl übergebe, dann wird es schwierig. Deshalb meine Frage: Gibt es eine Variablendefinition innerhalb der main, die der Compiler ad-hoc wählt, sobald er weiß, welcher Typ von der template-Funktion zurückgeliefert wird? Versteht ihr was ich meine?
Was passiert eigentlich wenn ich obige Funktion folgendermaßen aufrufe:
add(5,5.5)? Dann bekomm ich einen int zurück und die Nachkommastellen werden verschluckt? Hier liegt also der Programmierer in seiner Verantwortung, oder wie?
-
Ich denke es muss heissen
template<typename T, typename S> Test<T,S>::Test() : var1(0), var2(0) //Konstruktor 1 { }Zur 'weiteren Frage': Probiers halt mal aus

-
vip@r schrieb:
Test<T,S>::Test() : var1(0), var2(0) //Konstruktor 1 { } Test<T,S>::Test(T v1, S v2) : var1(v1), var2(v2) /Konstruktor 2 { }So wie es jetzt da steht meckert der Compiler mit 17 Fehler. Ich kann die Fehler hier jetzt nicht posten. Das sind zu viele. Wenn ich aber nun bei Konstruktor 2 das
Test<T,S>::weglasse, dann kompiliert er ohne zu meckern. Bei Konstruktor 1 macht das Vorhanden- oder nicht Vorhandensein von
Test<T,S>::keinen Unterschied. Dieser wird IMMER kompiliert.
-> Warum ist das so?
keine Ahnung. Das ist offensichtlich nicht exakt der Code, wie du ihn dem Compiler präsentierst. Da Compiler notwendigerweise pedantisch sind, müssen wir es auch sein.
-
Test<T,S>::Test(T v1, S v2) : var1(v1), var2(v2) /Konstruktor 2
{}
Das macht doch nicht einmal sinn. Keiner definiert, wie S und T ineinander konvertierbar sind.
Implementations-dateien sind soweit ich es verstanden habe nur ein anderer Header, der die Implementationen enthält (ich bspw. inkludiere ihn einfach nach der Klassendefinition direkt im Header).
ist der return-Wert ein int, da der erste Parameter des Funktionsaufrufs ein int ist
Das ist im Standard definiert - welche Typen Literale haben.
add(5.5,5)aufrufe, dann bekomm ich welchen Wert zurück? float oder double? Das verstehe ich nun nicht.
.
Deine Version dürfte gar nicht kompilieren, da hier der Template-parameter nicht deduziert werden kann.P.S.: Wenn du bei Fließkommazahlliteralen nicht explizit das f-Suffix angibst, dann (siehe Standard) ist es ein
double.