... must be a nonstatic member function.
-
Hi, ich habe hier einen Fehler bei dem ich einfach nicht weiterkomme:
Class Bla { //... float *operator [] ( unsigned int i ); } float Matrix:: *operator [] ( unsigned int i ){}
Ich bekomme hier die Fehlermeldung
" 'float Matrix:: *operator [] ( unsigned int i )' must be a nonstatic member function"
Habt bitte Erbarmen mit mir, wenn dieser Fehler allzu dämlich ist, programmiere erst seit gestern
-
Wozu ist das * bei
Matrix:: *operator []
gut?
BTW: Gerade die ersten Wochen sollte man probieren, selbstständig zu lernen. Alles, was man in diesem Stadium zu wissen braucht steht in dem Buch, das man hat (sofern es nicht von JW ist).
-
Sorry! Der Stern fehlte einfach weil ich ihn vergessen hatte. Ein Buch ist übrigens schon bestellt, ich muss diese Aufgabe blos für nächste Woche irgendwie fertig bekommen.
-
float* Matrix::operator[] ( unsigned int i ){}
-
Hey, danke erstmal!
Hätte noch eine Frage dazu: Wie kann ich eigentlich den Operator so verändern, dass ich auch zweidimensional arbeiten kann, also sozusagen auch nach a[][] verlangen kann. Ich arbeite zurzeit mit einem eindimensionalen Array, dass eigentlich eine Matrix ist. Daher wäre so eine zweidimensionale Ansicht schon sehr gut. Und nein, ich darf leider nicht mit Vector arbeiten
-
Unsaubere Variante:
class Matrix { public: //... float* operator[](size_t x) { return data+x*width; } const float* operator[](size_t x) const { return data+x*width; } private: size_t height, width; float *data; }
Saubere Variante:
class Matrix { public: //... class Proxy { explicit Proxy(float *data) float operator[](size_t y) { return data[y]; } const float& operator[](size_t y) const { return data[y]; } private: float *data; }; Proxy operator[](size_t x) { return Proxy(data+x*height); } const Proxy operator[](size_t x) { return Proxy(data+x*height); } private: size_t height, width; float *data; }
-
Hi,
funktioniert sehr gut! Habe die schlechtere Variante genommen Könntest du mir vielleicht noch kurz in wenigen Worten erklären was da vor sich geht? Warum kann ich bereits in der eigentlichen Deklaration den Operator umdefinieren? Und was passiert bei
" const float* operator[](unsigned int x) const { return matrix+x*columns; } "
überhaupt genau? (Hab's angepasst)
Vielen Dank für die gute Hilfe!
-
Dilkad schrieb:
Unsaubere Variante:
class Matrix { public: //... float* operator[](size_t x) { return data+x*width; } const float* operator[](size_t x) const { return data+x*width; } private: size_t height, width; float *data; }
Saubere Variante:
class Matrix { public: //... class Proxy { explicit Proxy(float *data) float operator[](size_t y) { return data[y]; } const float& operator[](size_t y) const { return data[y]; } private: float *data; }; Proxy operator[](size_t x) { return Proxy(data+x*height); } const Proxy operator[](size_t x) { return Proxy(data+x*height); } private: size_t height, width; float *data; }
Ich würde eher sowas nehmen:
Proxy Array::op[](int y){ return Proxy(this,y); } ... double& Array::Proxy::op[](int x){ return m_array->at(m_y,x); }
Dann kann man Array::at bauen, wie man will. Indexgrenzenprüfung, Sparse Matrix als Hashtable, x-Dimension compilezeitkonstand und/oder als Zweierpotenz, all das kann man so machen, ohne die op[][]-Sache damit belasten zu müssen.
Also ungefähr wie http://www.parashift.com/c++-faq-lite/operator-overloading.html#faq-13.11 , nur haben die IMHO nicht weit genug durchgezogen.
-
Indexgrenzenprüfung würde ich so machen wie es die STL macht, bei
[]
nicht, bei at() schon. Es bräuchte dann ein unchecked_at() mit einem assert. Die Idee ist nicht schlecht ...
Meine Version ist halt von Hand maximal optimiert, wenn der Compiler deine Version so stark optimieren könnte, dass sie identisch wären dann ist sie klar die bessere.
Dass deine flexibler wäre, stimmt allerdings nicht, mein Proxy kann auch template-Parameter (compilezeitkonstande x-Dimension) entgegennehmen und beliebig umgebaut werden, sie ist nur etwas eleganter.@Kladdi: wenn du nie in einer Funktion so etwas wie
const Matrix& m
entgegennimmst, kannst du diese Zeile ignorieren oder löschen (sie hat etwas mit Const-correctness zu tun).
-
Welchen genauen Zweck erfüllt die Proxy-Klasse?
-
Nick Unbekannt schrieb:
Welchen genauen Zweck erfüllt die Proxy-Klasse?
a=m[3][4]
ist
a=(m[3])[4]
oder
Proxy tmp=m[3];
a=tmp[4];
Sie erfüllt den Zweck, daß man einen operator[][] überhaupt anbieten kann. Das kann man nämlich gar nicht, weil C++ nur einen operator[] kennt. Mit dem Proxy-Trick gehts dann aber doch.
-
Okay ist klar. Danke für die Erläuterung.
-
Dilkad schrieb:
Indexgrenzenprüfung würde ich so machen wie es die STL macht, bei
[]
nicht, bei at() schon. Es bräuchte dann ein unchecked_at() mit einem assert.Warum nicht wie die STL machen und
at()
mit Exception undoperator[]
mitassert
? Von mir aus kann man auch aufat()
verzichten, ich hab das eh noch nie sinnvoll einsetzen können. Hier wäre einat()
höchstens praktisch, weil man gleich zwei Indizes angeben könnte.
-
Nexus schrieb:
Dilkad schrieb:
Indexgrenzenprüfung würde ich so machen wie es die STL macht, bei
[]
nicht, bei at() schon. Es bräuchte dann ein unchecked_at() mit einem assert.Warum nicht wie die STL machen und
at()
mit Exception undoperator[]
mitassert
? Von mir aus kann man auch aufat()
verzichten, ich hab das eh noch nie sinnvoll einsetzen können. Hier wäre einat()
höchstens praktisch, weil man gleich zwei Indizes angeben könnte.Ich meine at nicht als Funktion, die jemand anderes als der Proxy benutzen soll. Um den Proxy nur dummen Proxy sein zu lassen.
-
float acess_dilkad_matrix(a::matrix a, int x, int y) { return a[x][y]; } 0: 55 push %ebp 1: 89 e5 mov %esp,%ebp 3: 8b 45 08 mov 0x8(%ebp),%eax 6: c1 e0 02 shl $0x2,%eax 9: 0f af 45 14 imul 0x14(%ebp),%eax d: 8b 55 18 mov 0x18(%ebp),%edx 10: c1 e2 02 shl $0x2,%edx 13: 03 55 10 add 0x10(%ebp),%edx 16: d9 04 02 flds (%edx,%eax,1) 19: 5d pop %ebp 1a: c3 ret float access_parashift_matrix(b::matrix b, int x, int y) { return b(x, y); } 1b: 55 push %ebp 1c: 89 e5 mov %esp,%ebp 1e: 8b 55 14 mov 0x14(%ebp),%edx 21: 0f af 55 08 imul 0x8(%ebp),%edx 25: 03 55 18 add 0x18(%ebp),%edx 28: 8b 45 10 mov 0x10(%ebp),%eax 2b: d9 04 90 flds (%eax,%edx,4) 2e: 5d pop %ebp 2f: c3 ret float access_volkard_matrix(c::matrix c, int x, int y) { return c[x][y]; } 30: 55 push %ebp 31: 89 e5 mov %esp,%ebp 33: 83 ec 10 sub $0x10,%esp 36: 8b 45 14 mov 0x14(%ebp),%eax 39: ba 00 00 00 00 mov $0x0,%edx 3e: 89 45 f8 mov %eax,-0x8(%ebp) 41: 89 55 fc mov %edx,-0x4(%ebp) 44: df 6d f8 fildll -0x8(%ebp) 47: d9 7d f6 fnstcw -0xa(%ebp) 4a: 0f b7 45 f6 movzwl -0xa(%ebp),%eax 4e: b4 0c mov $0xc,%ah 50: 66 89 45 f4 mov %ax,-0xc(%ebp) 54: d9 6d f4 fldcw -0xc(%ebp) 57: db 5d f0 fistpl -0x10(%ebp) 5a: d9 6d f6 fldcw -0xa(%ebp) 5d: 8b 55 f0 mov -0x10(%ebp),%edx 60: 0f af 55 08 imul 0x8(%ebp),%edx 64: 03 55 18 add 0x18(%ebp),%edx 67: 8b 45 10 mov 0x10(%ebp),%eax 6a: d9 04 90 flds (%eax,%edx,4) 6d: c9 leave 6e: c3 ret
(erstellt mit
g++ -c --optimize a.cc && objdump -S a.o >a.S` ', Code hier verfügbar: http://codepad.org/dJptPyWr)
Ich werde wohl bei meiner Variante bleiben, obwohl die ()-Variante zwei left-shifts einspart. Ehrlich gesagt hat mich der C++-Compiler hier ein wenig enttäuscht...
-
Dilkad schrieb:
float acess_dilkad_matrix(a::matrix a, int x, int y) { return a[x][y]; } 0: 55 push %ebp 1: 89 e5 mov %esp,%ebp 3: 8b 45 08 mov 0x8(%ebp),%eax 6: c1 e0 02 shl $0x2,%eax 9: 0f af 45 14 imul 0x14(%ebp),%eax d: 8b 55 18 mov 0x18(%ebp),%edx 10: c1 e2 02 shl $0x2,%edx 13: 03 55 10 add 0x10(%ebp),%edx 16: d9 04 02 flds (%edx,%eax,1) 19: 5d pop %ebp 1a: c3 ret float access_parashift_matrix(b::matrix b, int x, int y) { return b(x, y); } 1b: 55 push %ebp 1c: 89 e5 mov %esp,%ebp 1e: 8b 55 14 mov 0x14(%ebp),%edx 21: 0f af 55 08 imul 0x8(%ebp),%edx 25: 03 55 18 add 0x18(%ebp),%edx 28: 8b 45 10 mov 0x10(%ebp),%eax 2b: d9 04 90 flds (%eax,%edx,4) 2e: 5d pop %ebp 2f: c3 ret float access_volkard_matrix(c::matrix c, int x, int y) { return c[x][y]; } 30: 55 push %ebp 31: 89 e5 mov %esp,%ebp 33: 83 ec 10 sub $0x10,%esp 36: 8b 45 14 mov 0x14(%ebp),%eax 39: ba 00 00 00 00 mov $0x0,%edx 3e: 89 45 f8 mov %eax,-0x8(%ebp) 41: 89 55 fc mov %edx,-0x4(%ebp) 44: df 6d f8 fildll -0x8(%ebp) 47: d9 7d f6 fnstcw -0xa(%ebp) 4a: 0f b7 45 f6 movzwl -0xa(%ebp),%eax 4e: b4 0c mov $0xc,%ah 50: 66 89 45 f4 mov %ax,-0xc(%ebp) 54: d9 6d f4 fldcw -0xc(%ebp) 57: db 5d f0 fistpl -0x10(%ebp) 5a: d9 6d f6 fldcw -0xa(%ebp) 5d: 8b 55 f0 mov -0x10(%ebp),%edx 60: 0f af 55 08 imul 0x8(%ebp),%edx 64: 03 55 18 add 0x18(%ebp),%edx 67: 8b 45 10 mov 0x10(%ebp),%eax 6a: d9 04 90 flds (%eax,%edx,4) 6d: c9 leave 6e: c3 ret
(erstellt mit
g++ -c --optimize a.cc && objdump -S a.o >a.S` ', Code hier verfügbar: http://codepad.org/dJptPyWr)
Ich werde wohl bei meiner Variante bleiben, obwohl die ()-Variante zwei left-shifts einspart. Ehrlich gesagt hat mich der C++-Compiler hier ein wenig enttäuscht...
Ich fürchte, der Test ist nicht fair.
Erstmal mache ich aux [x][y] das [y][x], wie dichter am nativen float[][] ist.
Dann mache ich bei volkard den Index zum size_t statt float.
Und ich ziehe den wichtigen data-Zeiger vor, damit er erstes (und im Zweifelsfall schnellstes) Attribut ist._Z20access_dilkad_matrixRN1a6matrixEii: .LFB1034: .cfi_startproc .cfi_personality 0x3,__gxx_personality_v0 movq 16(%rdi), %rax movslq %edx,%rdx movslq %esi,%rsi salq $2, %rsi addq (%rdi), %rsi salq $2, %rax imulq %rdx, %rax movss (%rsi,%rax), %xmm0 ret
_Z23access_parashift_matrixRN1b6matrixEii: .LFB1035: .cfi_startproc .cfi_personality 0x3,__gxx_personality_v0 movslq %edx,%rdx movq (%rdi), %rax movslq %esi,%rsi imulq 16(%rdi), %rdx addq %rsi, %rdx movss (%rax,%rdx,4), %xmm0 ret
_Z21access_volkard_matrixRN1c6matrixEii: .LFB1036: .cfi_startproc .cfi_personality 0x3,__gxx_personality_v0 movslq %edx,%rdx movq (%rdi), %rax movslq %esi,%rsi imulq 16(%rdi), %rdx addq %rsi, %rdx movss (%rax,%rdx,4), %xmm0 ret
Hmm.
Geschaut mit g++ -march=native -O3 -save-temps main.cpp
#include <iostream> #include <cstdlib> using namespace std; namespace a { class matrix { public: explicit matrix(size_t h, size_t w) : data(new float[h*w]),height(h), width(w) {} struct proxy { explicit proxy(float *data) : data(data){} float& operator[](size_t x) { return data[x]; } float *data; }; proxy operator[](size_t y) { return proxy(data+y*width); } private: float *data; size_t height, width; }; } namespace b { class matrix { public: explicit matrix(size_t h, size_t w) : data(new float[h*w]), height(h), width(w) {} float& operator()(size_t y, size_t x) { return data[y*width+x]; } private: float *data; size_t height, width; }; } namespace c { class matrix { public: explicit matrix(size_t h, size_t w) : data(new float[w*h]), height(h), width(w) {} struct proxy { explicit proxy(matrix *m, size_t y) : m(m), y(y) {} float& operator[](size_t x) { return m->unchecked_at(y,x); } matrix *m; size_t y; }; proxy operator[](size_t y) { return proxy(this, y); } float& unchecked_at(int y, int x) { return data[y*width+x]; } private: float *data; size_t height, width; }; } float access_dilkad_matrix(a::matrix& a, int x, int y) { return a[y][x]; } float access_parashift_matrix(b::matrix& b, int x, int y) { return b(y, x); } float access_volkard_matrix(c::matrix& c, int x, int y) { return c[y][x]; } int main(){ a::matrix m(10,10); for(int y=0;y!=10;++y) for(int x=0;x!=10;++x) m[y][x]=rand(); int y=rand()%10; int x=rand()%10; int t=access_dilkad_matrix(m,x,y); cout<<t<<'\n'; }
Ich werde wohl bei meiner Variante bleiben, weil mir scheint, der dumme Proxy kostet nichts, und innerhalb von unchecked_at sieht der Compiler im Zweifelsfall mehr Optimierungen, als wenn ich die Funktionalität auf mehrere Funktionen spalte. Und nicht zuletzt, es gefällt mir einfach, wenn der Proxy nur Proxy ist. So würde ich es auch machen, wenn das Compilat nur geringfügig langsamer wäre. Es ist typisch für mich, daß ich hübsche Sachen auch stehen lasse, wenn sie prinzipiell gut optimierbar sind, aber ich für den vollen Spaß noch eine oder zwei neue Compilerversionen abwarten muß.
Der Compiler hat mich wiedermal nicht enttäuscht.
-
volkard schrieb:
Erstmal mache ich aux [x][y] das [y][x], wie dichter am nativen float[][] ist.
Geschwindigkeitsmässig dürfte sich da nichts ändern, aber du hast Recht; hier verhält sich meine Matrix nicht wie ein
float[][]
, das dürfte nicht vor kommen.volkard schrieb:
Dann mache ich bei volkard den Index zum size_t statt float.
Oh, ein echter Bug. Das wird vermutlich der Grund für "dein" schlechtes Abschneiden in meinem Test sein.
volkard schrieb:
Und ich ziehe den wichtigen data-Zeiger vor, damit er erstes (und im Zweifelsfall schnellstes) Attribut ist.
Ganz ehrlich: das war ein (wieder mal zu früh umgesetzer) Trick um die
c::matrix
auszubremsen. Hier habe ich jedoch nicht wirklich tief nachgedacht, den das trifft ja alle gleich stark.volkard schrieb:
Hmm.
Bei meinem Compiler (g++ (Debian 4.4.5-8) 4.4.5) tritt der Vergleich weniger krass zu Tage - volkard unterscheidet sich an zwei Assemblerbefehlen von parashift, dilkard hat einen Befehl mehr - aber dass meine handoptimierte Matrix nicht nur gleichauf, nein sogar schlechter abschneidet als deine, das gibt mir zu denken.
volkard schrieb:
Und nicht zuletzt, es gefällt mir einfach, wenn der Proxy nur Proxy ist. So würde ich es auch machen, wenn das Compilat nur geringfügig langsamer wäre. Es ist typisch für mich, daß ich hübsche Sachen auch stehen lasse, wenn sie prinzipiell gut optimierbar sind, aber ich für den vollen Spaß noch eine oder zwei neue Compilerversionen abwarten muß.
Naja, ob der Proxy jetzt hübsch oder unschön ist, ist mir relativ egal, da der Benutzer es nicht mitbekommt/mitbekommen sollte. In klasseninterner Implementierung steht die Eleganz immer unter der Performanz, solange das Interface nicht darunter leidet (und das tut es bei der parashift-Methode).
Wenn ich aber weiss, dass eine volkard-Matrix besser ist, als meine jetzige, werde ich das in Zukunft berücksichtigen.
Stört mich nur noch eins:boost::multi_array<float, 2>
ist 11 mal langsamer als dilkad.Jetzt, da meine eigenen Code-Fehler ausgemerzt sind, kann ich dir nur zustimmen:
volkard schrieb:
Der Compiler hat mich wiedermal nicht enttäuscht.