Problem mit Arraywrapper
-
Ich habe eine Problem mit nem Arraywrapper.
template <int Dimensions = 2, class value = float> class Array { private: #if Dimensions == 1 value* Array; #elif Dimensions > 1 Array<Dimensions-1, value>* Array; #endif int Range; //... public: //... #if Dimensions == 1 value& #else Array<Dimensions-1, value>& #endif operator[](int element); //... }; template <int Dimensions, class value> #if Dimensions == 1 value& #else Array<Dimensions-1, value>& #endif Array<Dimensions, value>::operator[](int element) { if(element < Range) { return Array[element]; } else { throw exception(element + " is out of Range!"); } }
Soweit alles in Ordnung, wird so compiliert. Dann wollte ich das Ding Testen:
Array<2, float> Matrix(4, 0.0); try { Matrix[0][0] = 17.0; Matrix[0][1] = 87.0; Matrix[0][2] = 56.0; Matrix[0][3] = 84.0; Matrix[1][0] = 36.0; Matrix[1][1] = 45.0; Matrix[1][2] = 85.0; Matrix[1][3] = 79.0; Matrix[2][0] = 12.0; Matrix[2][1] = 65.0; Matrix[2][2] = 72.0; Matrix[2][3] = 92.0; Matrix[3][0] = 82.0; Matrix[3][1] = 60.0; Matrix[3][2] = 27.0; Matrix[3][3] = 95.0; } catch(exception& exce){std::cout << exce.what() << std::endl;} Matrix.sort();
Naja, dann bekomm ich in jeder Zeile folgenden Fehler:
Binaerer Operator '=' : Kein Operator definiert, der einen rechtsseitigen Operator vom Typ 'const double' akzeptiert (oder keine geeignete Konvertierung moeglich)
Ich hab keine Ahnung, was das heißen soll, nachdem die []-Ausdrücke augewertet wurden, müsste doch einer float-Referenz ein double-Wert zugewiesen werden können, oder nicht?
mfg
-
Glamdrink schrieb:
#if Dimensions == 1 value& #else Array<Dimensions-1, value>& #endif
#if #else #endif sind Präprozessordirektiven die im ersten Schritt des Kompilervorgangs ausgewertet werden, lange bevor deine Templates instanziert werden. Zum Instanzierungszeitpunkt ist diese Unterscheidung längst weg, und da am Anfang Dimensions gar nicht definiert ist, gewinnt immer der #else Zweig.
Du kannst das sehen, indem du einfach nur den Präprozessoroutput erzeugst. Standard ist die -E Option: g++ -E file.cpp
Du kannst das Problem lösen, indem du das Template für dimensions == 0 spezialisierst.
-
Ponto schrieb:
Du kannst das Problem lösen, indem du das Template für dimensions == 0 spezialisierst.
Das verstehe ich nicht, der Rest ist mir Klar, allerdings hab ich keine Ahnung, wie man beim Compiler die Aufrufkoventionen (mir ist das richtige Word grade nicht eingefallen) ändert. Ich habe einfach #else durch #elif Dimensions > 1 ersetzt, der Fehler bleibt allerdings (Obwohl es mich wundert, dass das Array so überhaupt kompiliert wird).
thx
-
Glamdrink schrieb:
Ponto schrieb:
Du kannst das Problem lösen, indem du das Template für dimensions == 0 spezialisierst.
Das verstehe ich nicht, der Rest ist mir Klar, allerdings hab ich keine Ahnung, wie man beim Compiler die Aufrufkoventionen (mir ist das richtige Word grade nicht eingefallen) ändert. Ich habe einfach #else durch #elif Dimensions > 1 ersetzt, der Fehler bleibt allerdings (Obwohl es mich wundert, dass das Array so überhaupt kompiliert wird).
thx
Das Problem kannst du nicht mit irgendwelchen Präprozessordirektiven lösen. Das ist unmöglich. Du hast dein N-dimensionales Array rekursiv definiert. Und wie bei allen Rekursionen benötigst du eine Abbruchbedingung. Diese kann man zum Beispiel für die Dimension 1 machen. Dazu wird das Template für diese Dimension spezialisiert. Das bedeutet, dass es für den Spezialfall eine andere Implementierung erhält.
Der folgende Code, der sich kompilieren lassen sollte, versucht dies zu verdeutlichen:
template <int Dimensions = 2, class value = float> class Array { private: Array<Dimensions-1, value> * _Array; int _Range; public: Array<Dimensions-1, value> & operator[](int element); }; template <typename value> class Array<1, value> { private: value * _Array; int _Range; public: value & operator[](int element); }; template <int Dimensions, class value> Array<Dimensions-1, value> & Array<Dimensions, value>::operator[](int element) { return _Array[element]; } template <typename value> value & Array<1, value>::operator[](int element) { return _Array[element]; } int main() { Array<3, float> array; array[2][3][2] = 1.0; }
-
ich würds noch etwas anders machen:
template<class Type,int Dimensions=1> class Array{ std::vector<Array<Type,Dimensions-1> > array; public: Array<Type,Dimensions-1>& operator[](unsigned int ElementID){ return array[ElementID]; } }; template<class Type> class Array<Type,1>{ std::vector<Type> array; public: Type& operator[](unsigned int ElementID){ return array[ElementID]; } };
is en bissl mehr c++ und es besteht keine gefahr in unallokiertem speicher zu schreiben
-
Eigentlich ist die ganze Klasse so sinnlos, da sie keine Vorteile gegenüber verschachtelten std::vector<> bietet.
-
wenn du lust hast
vector<vector<vector<vector<vector<Type> > > > > zu schreiben is das deine sache~~besides:
so wie das von dir vorgestellte array da steht, greift ihr immer auf den heap zu(bzw auf unallokierten speicher, ganz schlecht).
und jedes heap array kann besser mit einem vector dargestellt werden, da er genau dafür gemacht wurde.
wenn dir das nich gefällt, dann schreib den vector so um, dass er auf dem stack liegt(was imho nich soviel arbeit ist,wenn man von einem gleichmäßigen array ausgeht,dh alle dimensionen haben die gleiche ausdehnung),wobei du aber beachten muss, dass der stack ganz schnell voll ist.
-
otze schrieb:
so wie das von dir vorgestellte array da steht, greift ihr immer auf den heap zu(bzw auf unallokierten speicher, ganz schlecht).
und jedes heap array kann besser mit einem vector dargestellt werden, da er genau dafür gemacht wurde.Mein Code sollte nur die Technik demonstrieren und sonst nichts sinnvolles darstellen.
Tatsächlich würde ich bei kleinen Dimensionen und wenn es nicht um Speicher geht verschachtelte std::vector nutzen und sonst eine echte multidimensionale Arrayklasse. Es ist sinnlos die Größe für jede Dimension mehr als einmal zu speichern.