Überladen eines mehrdimensionalen Indexoperators [][]



  • Hallo liebe C++ Community!

    Ich hätte mal eine Frage.. ich soll den Zugriff auf eine 2x2 Matrix via [][] realisieren. Die Matrix ist dabei wie folgt aufgebaut (Umriss):

    Matrix2x2 hat ein mehrdimensionales double-Array (double[2][2]) in dem die Werte stehen. Wie kann ich nun erreichen, dass folgendes funktioniert:

    matrix2x2 m(1, 2, 3, 4);
    std::cout << m[1][0]; // soll "3" ausgeben.
    

    Vielen Dank,
    Christian



  • Du musst halt irgendein Proxy zurückgeben, im Groben:

    struct matrix2x2 
    {
    	struct proxy
    	{
    		friend class matrix2x2;
    
    		float operator[]( int idx ) const
    		{
    			return ptr[idx];
    		}
    
    	private:
    		proxy( const float* row ) : ptr( row )
    		{
    		}
    
    		const float* ptr;
    	};
    
    	matrix2x2( float a, float b, float c, float d )
    	{
    		mat[0] = a; mat[1] = b; 
    		mat[2] = c; mat[3] = d;
    	}
    
    	proxy operator[]( int idx ) const
    	{
    		return proxy( &mat[idx*2] );
    	}
    
    private:
    	float mat[4];
    };
    


  • Vielen Dank erstmal 🙂

    Habs angepasst und es funktioniert... allerdings würde ich gern verstehen, warum es funktioniert. Könntest du mir den Code vielleicht kurz erklären?

    Danke im Voraus,
    Christian



  • Ein 2d-Array verhält sich wie ein 1d-Array von 1d-Arrays. Du musst den [] operator so überladen, dass er ein 1d-Array zurück gibt. Es gibt keinen mehrdimensional Indexoperator:

    struct matrix2x2
    {
      double* operator [](int row){ return m_data[row]; }
    
      double m_data[2][2];
    };
    


  • Danke Stefan, das wusste ich (noch) nicht.

    Mein Code steht soweit... nun habe ich nur noch das Problem, dass ich nicht nur auf die Werte zugreifen, sondern sie auch ändern möchte.

    Also soll möglich sein:

    matrix2x2 m1(1,2,3,4);
    
    m1[0][0] = 99.5;
    

    Habe jetz schonmal den Rückgabewert von proxy auf proxy& geändert. Nun gibts allerdings den Fehler:

    "matrix2x2.h(33) : warning C4172: Adresse einer lokalen Variablen oder eines temporären Werts wird zurückgegeben"

    Ansonsten natürlich noch:

    main.cpp(32) : error C2106: '=': Linker Operand muss ein L-Wert sein

    Was muss ich ändern um die Referenz zu bekommen anstelle der Werte?

    Viele Grüße,
    Christian

    Die Header-Datei (steht das relevante drin)

    #ifndef MATRIX2X2_H
    #define MATRIX2X2_H
    
    #include <iostream>
    
    class matrix2x2 {
    
    public:
    
    	matrix2x2(double m1, double m2, double m3, double m4);
    
    	struct proxy {
    
    	public:
    
    		friend class matrix2x2;
    
    		double operator[](int idx) const {
    
    			return ptr[idx];
    
    		}
    
    	private:
    
    		proxy(const double *row) : ptr(row) {}
    		const double *ptr;
    
    	};
    
    	proxy& operator[](int idx) const {
    
    		return proxy(&matrix[idx*2]);
    
    	}
    
    	matrix2x2& operator+=(matrix2x2 const &rhs) {
    
    		this->matrix[0] += rhs[0][0];
    		this->matrix[1] += rhs[0][1];
    		this->matrix[2] += rhs[1][0];
    		this->matrix[3] += rhs[1][1];
    
    		return *this;
    
    	}
    
    	matrix2x2& operator*=(matrix2x2 const &rhs) {
    
    		this->matrix[0] *= rhs[0][0];
    		this->matrix[1] *= rhs[0][1];
    		this->matrix[2] *= rhs[1][0];
    		this->matrix[3] *= rhs[1][1];
    
    		return *this;
    
    	}
    
    	matrix2x2& operator*=(double const &rhs) {
    
    		this->matrix[0] *= rhs;
    		this->matrix[1] *= rhs;
    		this->matrix[2] *= rhs;
    		this->matrix[3] *= rhs;
    
    		return *this;
    
    	}
    
    private:
    
    	matrix2x2(double digit);
    	double matrix[4];
    
    };
    
    std::ostream &operator<<(std::ostream &os, const matrix2x2 &m);
    
    matrix2x2 operator+(const matrix2x2& lhs, const matrix2x2& rhs);
    matrix2x2 operator*(const matrix2x2& lhs, const matrix2x2& rhs);
    matrix2x2 operator*(const matrix2x2& lhs, const double& rhs);
    
    #endif
    


  • Dafür brauchst du im proxy noch eine nicht konstante Version von operator[]:

    float& operator[]( int idx ) 
    {
    	return ptr[idx];
    }
    

    Dass das funktioniert darf ptr nicht konstant sein, d.h. du musst auch den Konstruktor verändern:

    proxy(float *row) : ptr(row) {} 
    float *ptr;
    

    Dadurch funktioniert das konstruieren aus matrix2x2::operator[](...) const nichtmehr, da muss das const auch weg:

    proxy operator[](int idx) const { 
            return proxy(const_cast<float*>(&matrix[idx*2])); 
    }
    

    Und eine weitere Version (nicht konstant) muss her:

    proxy operator[]( int idx ) 
    {
    	return proxy( &matrix[idx*2] );
    }
    

    Leider ermöglicht das const gecaste dann sowas:

    void x( const matrix2x2& m )
    {
    	m[0][0] = 99.5f;
    }
    

    Also unbedingt explizit einen konstanten Proxy bei matrix2x2::operator[](...) const zurückgeben:

    const proxy operator[](int idx) const { 
            return proxy(const_cast<float*>(&matrix[idx*2])); 
    }
    

    Oder, zwischem konstanten und nicht konstantem Proxy unterscheiden:

    // ...
    
    struct const_proxy
    {
    	friend class matrix2x2; 
    
    	float operator[](int idx) const 
    	{ 
    		return ptr[idx]; 
    	} 
    
    private:
    	const_proxy( const float* row ) : ptr( row )
    	{
    	}
    
    	const float* ptr;
    };
    
    struct proxy
    { 
    public: 
    	friend class matrix2x2; 
    
    	float operator[](int idx) const 
    	{ 
    		return ptr[idx]; 
    	} 
    
    	float& operator[]( int idx ) 
    	{
    		return ptr[idx];
    	}
    
    private: 
    	proxy(float *row) : ptr(row) {} 
    	float *ptr; 
    }; 
    
    const_proxy operator[](int idx) const 
    { 
    	return const_proxy(&matrix[idx*2]); 
    } 
    
    proxy operator[]( int idx ) 
    {
    	return proxy( &matrix[idx*2] );
    }
    
    // ...
    

    Oder halt ohne Proxy, wie __Stefan__ vorgeschlagen hat:

    const float* operator[](int idx) const 
    { 
    	return &matrix[idx*2]; 
    } 
    
    float* operator[]( int idx ) 
    {
    	return &matrix[idx*2];
    }
    


  • Danke erstmal für eure Mühen und ausführlichen Antworten!

    Ich hab jetzt erstmal das ausprobiert, was du an Änderungen vorgeschlagen hast. Es führte leider zum Selben "Fehler": error C2106: '=': Linker Operand muss ein L-Wert sein

    Bevor ich jetzt deine 2. Version ausprobiere: das was Stefan vorgeschlagen hat, ermöglicht mir dann allerdings nur einen Zugriff âla m[2] und nicht m[1][0] - oder?

    Viele Grüße,
    Christian

    Edit: Deine 2. Version funktioniert übrigens schon tadellos - Danke auch dafür!



  • Doch, alle Ansätze erlauben den Zugriff per m[row][column]. Und wegen des Fehlers: Du hast vermutlich irgendwas vergessen zu ändern. 🙂



  • David_pb schrieb:

    Doch, alle Ansätze erlauben den Zugriff per m[row][column]. Und wegen des Fehlers: Du hast vermutlich irgendwas vergessen zu ändern. 🙂

    Hmm.. ich versteh langsm C++ nicht mehr :D.

    Für mich sieht der Code von Stefan so aus, dass ich dort einen Wert "row" übergebe. Woher weiß C++, dass ich mit der Angabe m[1][1] quasi m[3] meine?

    double* operator [](int row){ return m_data[row];
    

    Und: wo genau läge der Vorteil deines Codes, der deutlich komplexer erscheint?
    (Sorry für die Fragerei.. möchte halt nix einbauen, was ich nicht verstehe :))

    Nachtrag: Hab einmal vergessen eine Referenz zu setzen. Deine beiden Lösungen funktionieren einwandfrei!



  • m_data muss in diesem Fall natuerlich ein 2 Dimensionales Array sein.

    Aber mach es bitte mit einem Proxy. Dann verstehst du 1) den Sinn und 2) fasst man Zeiger nicht so nackt an.

    der operator[] liefert einfach ein Objekt dass selber einen operator[] anbietet:
    langschreibweise fuer m[1][2] waere:

    matrix.operator[](1).operator[](2);
    oder noch laenger:

    proxy = matrix.operator[](1);
    proxy.operator[](2);



  • Ok, danke :).

    Aber dennoch frag ich mich, woher C++ weiß, was ich mit m[1][1] meine. Also Stefan hat mir ja schon erklärt, dass ich auf mehrdimensionale Arrays via einem Index zugreifen kann.. Ist es denn grundsätzlich in C++ so, dass es auch umgekehrt funktioniert? Weil nach meinem Verständnis frisst der überladene Operator von Stefan ja nur den ersten Index.. ich frag mich halt, wie er von [1][1] automatisch auf 3 kommt...

    LG,
    Chris



  • harakiri schrieb:

    Aber dennoch frag ich mich, woher C++ weiß, was ich mit m[1][1] meine.

    C++ weiss garnichts.
    Du hast ein m Objekt.
    Und du rufst darauf den operator[] auf.
    der operator[] ist ja eine normale Funktion und returned irgendein objekt.
    Und auf diesem Objekt rufst du dann wieder den operator[] auf.

    m[1][1] ist kurz fuer: m.operator[](1).operator[](1)

    Stell dir statt operator[] einfach getElement() vor:
    e=m.getElement(1);
    e.getElement(1);

    oder eben kurz: m.getElement(1).getElement(1)


Anmelden zum Antworten