class-template: falscher Typ wird ausgewählt
-
Hallo zusammen,
ich habe ein template für eine Grid-Klasse, für int, float oder double.template<typename T> class Grid {...}; class Exe{ Grid<float> grid1; Exe(...){ grid1 = Grid<float>(); }... };Wenn ich jetzt eine Instanz von Exe erzeuge, wird sofort der Standard-Konstruktor von Grid aufgerufen, noch bevor die Zuweisung erreicht wird, und konsequenter Weise nicht mit dem richtigen Typen! Es wird dann zwar ein Grid<float> erzeugt, aber der operator= wird von der anderen Klasse (in dem Fall Grid<double>) verwendet!
Kann mir das vielleicht jemand erklären?
Danke!
Uwe
-
Deine Exe Klasse enthält eine Grid Objekt
Grid<float> grid1;wenn Du also eine instanz von EXE Erzeugst, wird beim aufruf des Kosntruktors von EXE zuvor der Standard Konstruktor von Grid aufrufen. Du kannst aber nun einen definierten Konstruktor von dir über die Initialisierungsliste von EXE Konstruktor aufrufen:
template<typename T> class Grid {...}; class Exe{ Grid<float> grid1; Exe(...) : grid1(....){ //hier die Parameter für den grid ctor angeben }... };oder du deklarierst in deiner EXE klasse einen zeiger auf Grid,
und weist diesem deine grid Isntanz im EXE Konstruktor zu:template<typename T> class Grid {...}; class Exe{ Grid<float> *grid1; Exe(...){ grid1 = new Grid<float>(); }... };Denke das war dein Problem.
P.S.: Hoffe ich liege richtig hab länger nich mehr mit c++ handtiert;)
-
Danke erstmal für die Antwort!
Die dynamische Variante würde sicher funktionieren, möchte ich aber gerne vermeiden, schon wegen der Eleganz bei der Verwendung von Operatoren.
Das mit der Initialisierungliste schien mit vielversprechend, habe ich ausprobiert, führt aber leider nicht zum gewünschten Ergebnis!
Nochmal etwas ausführlicheren code dazu - der ctor von Execution sieht so aus:Execution::Execution(string name, Controller* mC): arealGrid(Grid<int>()), storPercGrid(Grid<float>()), inputGrid(Grid<float>("float", mC)), evapGrid(Grid<float>("float", mC)){ ...}weitere Grids sind nicht als Instanzvariablen deklariert.
Grid.h sieht so aus:#ifndef _Grid_h_ #define _Grid_h_ #include "Matrix.h" #include "ReportException.h" template<typename T> class Grid{ public: //Constructors: Grid(); // Standard- constructor Grid(string, Controller*); //Grid(string, int, string, Controller*); Grid(string, string, Controller*); Grid(const Grid<T> &); // copy-Constructor // Destructor ~Grid(); // operators: Grid<T>& operator=(const Grid<T>&); const T& operator()(int, int)const; T& operator()(int, int); //Methods: void setValue(T); void writeGrid (string); int sizeX(); int sizeY(); // Variables: private: //Methods: void setHeader(); void read(string); //method for reading in of data // Variables: int xSize; //number of columns int ySize; //number of rows double xllcorner; double yllcorner; double cellsize; int noData; Controller* gridCont; string varType; //type of values in the grid: "int" or "double" T* data; }; #endifund Grid.cpp (nur die ctors) so:
... template class Grid<int>; template class Grid<float>; template class Grid<double>; template<typename T> Grid<T>::Grid(){ data = 0; this->xSize = 0; this->ySize = 0; this->cellsize = 0; this->gridCont = 0; this->noData = 0; this->xllcorner = 0; this->yllcorner = 0; } template<typename T> Grid<T>::Grid(string type, Controller* grCont){ this->gridCont = grCont; this->xSize = this->gridCont->getInt("xSize"); this->ySize = this->gridCont->getInt("ySize"); this->data = new T[this->xSize * this->ySize]; this->varType = type; this->setHeader(); } template<typename T> Grid<T>::Grid(string filename, string valType, Controller* grCont){ this->gridCont = grCont; this->setHeader(); this->data = 0; this->read(filename); } template<typename T> Grid<T>::Grid(const Grid<T>& rhs): gridCont (rhs.gridCont), xSize (rhs.xSize), ySize (rhs.ySize), xllcorner (rhs.xllcorner), yllcorner (rhs.yllcorner), cellsize (rhs.cellsize), noData (rhs.noData), varType (rhs.varType){ if (rhs.data != 0){ this->data = new T[rhs.xSize * rhs.ySize]; for (int y = 0; y < rhs.ySize; y++){ for (int x = 0; x < rhs.xSize; x++){ this->data[y * rhs.xSize + x] = rhs.data[y * rhs.xSize + x]; } } } } template<typename T> Grid<T>::~Grid(){ delete [] this->data; this->data = 0; }Bei der Erstellung des Execution-Objekts werden jetzt folgende Grid-Konstruktoren aufgerufen:
- Grid<double>() - Grid<double>() - Grid<float>(string, Controller*) - Grid<float>(const Grid<T>&) - ~Grid<double>() - Grid<float>(string, Controller*) - Grid<float>(const Grid<T>&) - ~Grid<double>()Einerseits verstehe ich nicht, warum er Grid<double> erzeugt, sowas gibts in der ganzen Execution-Klasse nicht. Schön - er löschts wieder. Aber wo bleibt mein Grid<int>? Das interpretiert er anscheinend als float! Dabei ist arealGrid in Execution.h laut und deutlich als Grid<int> deklariert, und der Aufruf ist doch auch ganz explizit mit int!
Später werden dann auch auf alle Grid<int> die Methoden/Operatoren von Grid<float> angewandt, und in T *data steht dann (z.B. nach kopieren) Müll!
ratlos,
Uwe
-
Also beim ersten Überblick sehe ich eigentlich nichts, was eine implizite Umwandlung verschiedener Grid-Varianten benötigt (oder überhaupt möglich macht). Aber bei der Initialisierungsliste kannst du auch direkt die erforderlichen Parameter mitgeben:
Execution::Execution(string name, Controller* mC): arealGrid(), storPercGrid(), inputGrid("float", mC), evapGrid("float", mC) { ... }Allerdings fällt auf, daß die beiden expliziten Ctor'en nur einen Teil der Member mit Werten füllen.
(und der Default-Ctor legt kein Daten-Array an, also kannst du auch nicht darauf zugreifen, solange du das nicht nachgeholt hast)
-
Das mit der veränderten Ini-liste ist super, klar, das gibt weniger Aufwand und kein unnötiges kopieren. Hab ich so eingebaut.
Das Initialisieren der Member erfolgt teilweise in der Methode setHeader(), diese grieft dazu auf eine Datei zu, in der die meisten benötigten Werte stehen. Dort erfahre ich auch erst die notwendige Größe für T *data, deswegen wird es im std-ctor nicht schon erzeugt. Ein mit std-ctor erzeugtes Grid soll so nicht verwendet werden, ich dachte, daß die "Komplettierung" über eine spätere Zuweisung von Grids möglcih wäre á laGrid<int> arealGrid = Grid<int>(); arealGrid = Grid<int>(Controller* x);?
Eine Umwandlung eines Grid-typs in einen anderen ist tatsächlich nicht geplant.Ich habe jetzt festgestellt, daß der ctor Grid(Controller*) (war vorher noch ein jetzt überflüssiger string mit drin) immer den richtigen Typen auswählt, der std-ctor aber nicht. Wenn ich Grid(Controller*) jetzt immer in der ini-liste aufrufe, funktioniert das soweit, weiß der Geier warum. Aber:
Execution::Execution(Controller* mC): arealGrid(mC), storPercGrid(mC), inputGrid(mC), evapGrid(mC){ arealGrid = arealGrid; ...}Wenn er die Zuweisung erreicht, zeigt er brav zwei Grid<int> an. Wenn er in den Operator springt, sieht er plötzlich zwei Grid<float>!
Also eigentlich noch das gleiche Problem! Wonach entscheidet sich denn, welcher Operator aufgerufen wird? Ich dachte, nach dem Typen, auf den er angewendet wird? Offensichtlich hat es auch noch was mit dem Inhalt der Methode (bzw. Konstruktor, Operator) zu tun?
Stimmt da was mit dem =Operator nicht? Sieht so aus:template<typename T> Grid<T> &Grid<T>::operator=(const Grid<T> & rhs){ /* '=' operator to write something like: Grid grid1 = Grid(); grid1 = grid2 */ if (this != &rhs){ this->gridCont = rhs.gridCont; this->xSize = rhs.xSize; this->ySize = rhs.ySize; this->xllcorner = rhs.xllcorner; this->yllcorner = rhs.yllcorner; this->cellsize = rhs.cellsize; this->noData = rhs.noData; delete [] this->data; this->data = 0; if (rhs.data != 0){ this->data = new T[rhs.xSize * rhs.ySize]; for (int y = 0; y < rhs.ySize; y++){ for (int x = 0; x < rhs.xSize; x++){ this->data[y * rhs.xSize + x] = rhs.data[y * rhs.xSize + x]; } } } } return *this; }ich bin weiter dankbar für alle Vorschläge!
Uwe