... 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 und operator[] mit assert ? Von mir aus kann man auch auf at() verzichten, ich hab das eh noch nie sinnvoll einsetzen k├Ânnen. Hier w├Ąre ein at() 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 und operator[] mit assert ? Von mir aus kann man auch auf at() verzichten, ich hab das eh noch nie sinnvoll einsetzen k├Ânnen. Hier w├Ąre ein at() 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.

    ­čÖé


Log in to reply