PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : [VS2010] Problem mit Templates


Locutus2002
2012-08-25, 10:42:19
Hallo,

Ich habe durch mein Mathe-Studium, wo C++-Grundlagen-Veranstaltungen Pflicht sind, an C++ "Blut geleckt" und beschäftige mich nun mit etwas fortgeschrittenen Themen, die nicht Teil der Vorlesungen waren, unter anderem Templates.

Nun habe ich mit Klassen-Templates folgendes Problem:
Es handelt sich um ein einfaches Beispiel in Form einer Konsolenanwendung. Wie sonst auch bei Klassen, habe ich die (Template-)Deklaration der Klasse in eine einzelne Header-Datei geschrieben und die Methoden-Implementierungen in eine entsprechende cpp-Datei. Aufrufe erfolgen dann über ein Rahmen-/Test-Programm.
Wenn ich das Programm dann ausführen will, erhalte ich keine Kompilier-Fehler, dafür aber Linker-Fehler:

1>------ Erstellen gestartet: Projekt: Array-Template, Konfiguration: Release Win32 ------
1>Der Buildvorgang wurde am 25.08.2012 10:27:14 gestartet.
1>InitializeBuildStatus:
1> "Release\Array-Template.unsuccessfulbuild" wird erstellt, da "AlwaysCreate" angegeben wurde.
1>ClCompile:
1> ArrayTemplate.cpp
1> Main.cpp
1>Main.obj : error LNK2001: Nicht aufgelöstes externes Symbol ""public: __thiscall Array<class Animal>::Array<class Animal>(int)" (??0?$Array@VAnimal@@@@QAE@H@Z)".
1>Main.obj : error LNK2001: Nicht aufgelöstes externes Symbol ""public: __thiscall Array<int>::Array<int>(int)" (??0?$Array@H@@QAE@H@Z)".
1>F:\Documents\Visual Studio 2010\Projects\Array-Template\Release\Array-Template.exe : fatal error LNK1120: 2 nicht aufgelöste externe Verweise.
1>
1>Fehler beim Erstellen
1>
1>Verstrichene Zeit 00:00:00.93
========== Erstellen: 0 erfolgreich, Fehler bei 1, 0 aktuell, 0 übersprungen ==========


Kommentiere ich die Konstruktor-Aufrufe im Rahmenprogramm aus, tritt der Fehler nicht auf (was mir natürlich nichts nützt, wenn ich eben diese testen will). Per Zufall habe ich entdeckt, dass der Fehler nicht auftritt, wenn ich die Implementierungen in die Header-Datei oder ins Rahmenprogramm verschiebe, statt eine gesonderte Code-Datei zu verwenden.

Jetzt die Frage: Wieso? Und wie bekomme ich es hin, dass es "wie üblich" funktioniert, also mit gesonderten h- und cpp-Dateien?

Locutus2002
2012-08-25, 11:58:12
Ein weiteres Problem: Wenn ich in der Deklaration einer Template-Klasse eine Friend-Funktion angebe, führt dies auch zu einem Linker-Fehler, allerdings unabhängig davon, wo genau die Implementierung dieser Funktion stattfindet.

Auch hier die Frage: Wieso?

Ganz allgemein gefragt: Hat sich seit 2000 im ANSI-Standard irgend etwas bezüglich Templates geändert? (Das Buch, das ich aus der Hochschulbibliothek habe, ist nämlich von 2000, neuere Auflagen sind noch vergriffen).

ScottManDeath
2012-08-25, 11:59:26
Das geht nur wenn Du explizit das Template in einer .cpp Datei fuer alle Typen instanzierst.

Ansonsten ist es ueblich das alles in einer Header Datei zu haben.

Locutus2002
2012-08-25, 12:05:49
Das geht nur wenn Du explizit das Template in einer .cpp Datei fuer alle Typen instanzierst.

Ansonsten ist es ueblich das alles in einer Header Datei zu haben.

Ich nehme an, dass das die Lösung für mein ursprüngliches Problem ist?! Danke, das hilft schonmal. Dann packe ich Implementierungen für Template-Klassen jetzt auch in die Header-Dateien mit rein. Mit dem ersten Satz kann ich ehrlich grad nichts anfangen. Wie muss ich mir das vorstellen (nicht vergessen: ich bin kein Informatiker/Profi sondern nur Laie)? Heißt das, ich muss erst für alle erdenklichen Typen Objekte erstellen (=instanzieren)? Oder habe ich das Wort "instanzieren" falsch verstanden? Kannst Du ein Code-Beispiel geben, wie das aussehen soll?

Trap
2012-08-25, 12:10:52
Templates kann man nicht linken. Man hat es in der Sprachgeschichte von C++ mit "export templates" mal probiert, das ist aber grandios gescheitert (es hat genau 1 Compiler mal das Feature implementiert um zu zeigen wie absurd umständlich es ist, danach wurde das Feature wieder entfernt).

Locutus2002
2012-08-25, 12:26:16
Also heißt das, dass ich, wie von ScottManDeath vorgeschlagen, alles in die Header-Datei schreiben sollte?!

Gibt es eine entsprechende Lösung für das Friend-Funktionen-Problem in meinem zweiten Post? Da hilft genau das nämlich nicht.

Oder sind Templates generell ein veraltetes Konzept, das nicht mehr verwendet werden sollte?

ShadowXX
2012-08-25, 12:39:47
Es wäre einfacher wenn du uns den Source zeigen würdest.

Das folgende Beispiel kompiliert sowohl VS2008 als auch VS2010 ohne Probleme, vielleicht hilt dir das.

Beispiel:

template <typename T>
void CreateDummyDataVector(vector<T>& dummyVec);



template <typename T>
void CXXXXClass::CreateDummyDataVector(vector<T>& dummyVec)
{
dummyVec.clear();
T data;
memset(&data, 0, sizeof(T));
dummyVec.push_back(data);
}


P.S.
nein, Templates sind kein veraltetes Konzept.

Locutus2002
2012-08-25, 12:48:28
Das Friend-Problem habe ich nun nach längerer Recherche lösen können:

#include <ostream>
using std::ostream;

const int DefaultSize = 10;

template<class T> class Array;
template<class T> ostream & operator<<(ostream &, Array<T> &);
template<class T>
class Array
{
public:
Array(int itsSize = DefaultSize);
Array(const Array & rhs);
~Array(void) {delete [] pType;}

Array & operator=(const Array & rhs);
T & operator[](int offset) {return pType[offset];}
const T & operator[](int offset) const {return pType[offset];}

int GetSize(void) const {return itsSize;}

friend ostream & operator<< <> (ostream &, Array<T> &);

private:
T *pType;
int itsSize;
};

Der Schlüssel war, die Friend-Funktion vor dem Klassen-Template zu deklarieren und davor wiederum anzugeben, dass man vorhat die Klasse zu "templatisieren".

Die Implementierungen habe ich dann in dieselbe Datei, also den Header, geschrieben

Nasenbaer
2012-08-26, 23:43:09
Templates sind zwar nicht veraltet aber doch etwas speziell in ihrer Handhabung aber bieten halt die Möglichkeit generischen Code dennoch typsicher zu gestalten. Nicht immer sind Templates aber das richtige für jedes Problem.

Hat sich mit C++11 eigentlich was in Bezug auf Templates gebessert? Also z.B. Wildcards wie bei Java Generics?

Expandable
2012-08-27, 19:33:27
Hat sich mit C++11 eigentlich was in Bezug auf Templates gebessert? Also z.B. Wildcards wie bei Java Generics?

Ja, es hat sich was getan: Du kannst jetzt vector<list<int>> schreiben statt vector<list<int> > :freak: Ne ernsthaft, Templates mit einer beliebigen Anzahl an Templateparamtern ("variadic templates") werden jetzt unterstützt; das ist schon eine relativ wichtige Änderung für manche Use Cases. Ach ja, und ich manchen Fällen war es früher nicht möglich, den Rückgabewert einer Templatefunktion anzugeben; das geht jetzt auch.

Concepts sind wohl das, was in die Richtung der Java Wildcards geht. Die sind aber nicht Teil des Standards, also Nein zum zweiten Teil der Frage.

Nasenbaer
2012-08-28, 09:46:25
Ohh fein, keine Verwechslung mehr mit dem Shift-Operator, wow! :D
Naja das andere brauch ich weniger - schade, dass sich da wenig getan hat. Aber vielleicht fehlt mir in manchen C++ Bereichen auch einfach die Erfahrung um eine elegante Lösung für manche meiner Probleme zu erkennen.

Coda
2012-08-28, 10:15:47
Also z.B. Wildcards wie bei Java Generics?
Das ergibt bei Templates keinen Sinn. Generics sind etwas fundamental anderes, auch wenn der Syntax ähnlich aussieht.

Nasenbaer
2012-08-28, 10:22:31
Nungut ich fänd es aber praktisch, wenn man spezifizieren könnte, dass nur bestimmte Typen, die bspw. von einer genannten Klasse ableiten, zulässig sind. Das würde ihrer Verwendung manchmal intuitiver machen, glaub ich. ^^
Natürlich wird zur Compilertime gecheckt ob der Typ kompatibel ist, also die entsprechenden Methoden mit korrekter Signatur aufweißt, wenn dieser innerhalb des Templates verwendet werden. Aber ich fänds dennoch irgendwie intuitiver.
Ein konkreter Fall, wo dieses fehlende Feature aber ein showstopper ist fällt mir auf Anhieb nicht ein. Kann aber sein, dass ich schonmal so ein Problem hatte, und daher irgendwie den Wunsch nach Wildcards im Hinterkopf gespeichert hab. :D

Coda
2012-08-28, 10:25:34
Nungut ich fänd es aber praktisch, wenn man spezifizieren könnte, dass nur bestimmte Typen, die bspw. von einer genannten Klasse ableiten, zulässig sind.
https://en.wikipedia.org/wiki/Concepts_(C%2B%2B)

Wurde nicht in C++ 2011 aufgenommen. Das nächste Mal vielleicht.

Nasenbaer
2012-08-29, 13:40:04
https://en.wikipedia.org/wiki/Concepts_(C%2B%2B)

Wurde nicht in C++ 2011 aufgenommen. Das nächste Mal vielleicht.
Habe durch Zufall ne Alternative gefunden: Type traits (http://en.cppreference.com/w/cpp/types) und static_assert (http://en.cppreference.com/w/cpp/language/static_assert).

Hab mal ein Beispiel dazu geschrieben:

#include <type_traits>

using namespace std;

class Base {};
class Derived : public Base {};
class BaseOther {};

template<class T>
class Bla
{
static_assert( is_base_of<Base, T>::value, "T must inherit from Base");
public:
T data;
};

int main(int argc, char** argv)
{
Bla<Base> test; // OK
Bla<Derived> test2; // OK
Bla<BaseOther> test3; // Compile time error because BaseOther does not inherit from Base
}


Sind ja doch einige richtig tolle Sachen in C++11 aufgenommen worden. =)