overloading [][] operators in c++

后端 未结 5 1352
小蘑菇
小蘑菇 2020-12-11 11:39

I\'m writing a matrix 3x3 class in c++.

glm::mat3 provides access to matrix data through the [][] operator syntax.
e.g. myMatrix[0][0] = 1.0f;

相关标签:
5条回答
  • 2020-12-11 11:49

    There is no operator [][], so you need to overload the [] operator twice: once on the matrix, returning a surrogate object for the row, and once for the returned surrogate row:

    // Matrix's operator[]
    const row_proxy operator[](int row) const
    {
        return row_proxy(this, row);
    }
    // Proxy's operator[]
    const real operator[](int col) const
    {
        // Proxy stores a pointer to matrix and the row passed into the first [] operator
        return ((this->row >= 0 && this->row <= 2) && (col >= 0 && col <= 2)) ? this->matrix->_data[this->row][col] : 0.0f;
    }
    
    0 讨论(0)
  • 2020-12-11 11:58

    Generally, for multiple argument you want to use operator(), not operator[].

    Hm, in case it wasn't obvious, there is no operator[][] in C++. It's just operator[] applied twice. Which means that if you want that notation, then you have to let the first one return a result (indexable thing or proxy) that the second can be applied to.

    The code below sketches some approaches, choose what you like:

    #include <iostream>
    #include <vector>
    
    template< int n >
    int& dummy() { static int elem = n; return elem; }
    
    struct Mat1
    {
        int operator() ( int const x, int const y ) const
        { return dummy<1>(); }
    
        int& operator() ( int const x, int const y )
        { return dummy<1>(); }
    
        Mat1( int, int ) {}
    };
    
    struct Mat2
    {
        int at( int const x, int const y ) const
        { return dummy<2>(); }
    
        int& at( int const x, int const y )
        { return dummy<2>(); }
    
        Mat2( int, int ) {}
    };
    
    struct Mat3
    {
        struct At { At( int x, int y ) {} };
    
        int operator[]( At const i ) const
        { return dummy<3>(); }
    
        int& operator[]( At const i )
        { return dummy<3>(); }
    
        Mat3( int, int ) {}
    };
    
    class Mat4
    {
    protected:
        int get( int const x, int const y ) const
        { return dummy<4>(); }
    
        void set( int const x, int const y, int const v ) {}
    
        class AssignmentProxy
        {
        private:
            Mat4*   pMat_;
            int     x_;
            int     y_;
        public:
            void operator=( int const v ) const
            { pMat_->set( x_, y_, v ); }
    
            int value() const { return pMat_->get( x_, y_ ); }
            operator int () const { return value(); }
    
            AssignmentProxy( Mat4& mat, int const x, int const y )
                : pMat_( &mat ), x_( x ), y_( y )
            {}
        };
    
    public:
        int operator()( int const x, int const y ) const
        { return get( x, y ); }
    
        AssignmentProxy operator()( int const x, int const y )
        { return AssignmentProxy( *this, x, y ); }
    
        Mat4( int, int ) {}
    };
    
    class Mat5
    {
    protected:
        int at( int const x, int const y ) const
        { return dummy<4>(); }
    
        int& at( int const x, int const y )
        { return dummy<5>(); }
    
        class RowReadAccess
        {
        private:
            Mat5 const* pMat_;
            int         y_;
    
        public:
            int operator[]( int const x ) const
            {
                return pMat_->at( x, y_ );
            }
    
            RowReadAccess( Mat5 const& m, int const y )
                : pMat_( &m ), y_( y )
            {}
        };
    
        class RowRWAccess
        {
        private:
            Mat5*   pMat_;
            int     y_;
    
        public:
            int operator[]( int const x ) const
            {
                return pMat_->at( x, y_ );
            }
    
            int& operator[]( int const x )
            {
                return pMat_->at( x, y_ );
            }
    
            RowRWAccess( Mat5& m, int const y )
                : pMat_( &m ), y_( y )
            {}
        };
    
    public:
        RowReadAccess operator[]( int const y ) const
        { return RowReadAccess( *this, y ); }
    
        RowRWAccess operator[]( int const y )
        { return RowRWAccess( *this, y ); }
    
        Mat5( int, int ) {}
    };
    
    struct Mat6
    {
    private:
        std::vector<int>    elems_;
        int                 width_;
        int                 height_;
    
        int indexFor( int const x, int const y ) const
        {
            return y*width_ + x;
        }
    
    public:
        int const* operator[]( int const y ) const
        {
            return &elems_[indexFor( 0, y )];
        }
    
        int* operator[]( int const y )
        {
            return &elems_[indexFor( 0, y )];
        }
    
        Mat6( int const w, int const h )
            : elems_( w*h, 6 ), width_( w ), height_( h )
        {}
    };
    
    int main()
    {
        using namespace std;
        enum{ w = 1024, h = 1024 };
        typedef Mat3::At At;
    
        Mat1 m1( w, h );
        Mat2 m2( w, h );
        Mat3 m3( w, h );
        Mat4 m4( w, h );
        Mat5 m5( w, h );
        Mat6 m6( w, h );
    
        wcout
            << m1( 100, 200 )       // No fuss simple, but exposes element ref.
            << m2.at( 100, 200 )    // For those who don't like operators.
            << m3[At( 100, 200)]    // If you really want square brackets mnemonic.
            << m4( 100, 200 )       // Hides element ref by using assignment proxy.
            << m5[200][100]         // Ditto but with square brackets (more complex).
            << m6[200][100]         // The minimum fuss square brackets, exposes elem ref.
            << endl;
    }
    

    Oh well I discovered after posting that code that I haven't fully hidden the internal storage for Mat5: it needs an extra proxy level, as in Mat4. So that approach is really complex. I wouldn't do it (Mat1 is nice and easy I think), but some folks think proxys are cool, and data hiding even more cool…

    Summing up, there is no “the” correct way to overload operator[]. There are many ways (as illustrated by the code above), each with some trade-offs. And generally you’re better off using operator(), because as opposed to operator[] it can take any number of arguments.

    0 讨论(0)
  • 2020-12-11 12:12

    There is no [][] operator. The way GLM does it is by returning a vec3& from the first []. vec3 has its own [] operator overload. So it's two separate operator[]s on two separate classes.

    This is also how GLSL works. The first [] gets the column as a vector. The second takes the vector and gets the value from it.

    0 讨论(0)
  • 2020-12-11 12:13

    It would be easier to make the method double operator() (int row, int col) const. Instead of matrix[i][j] you just say matrix(i,j).

    0 讨论(0)
  • 2020-12-11 12:13

    The expression foo[1][2] is really interpreted as (foo[1])[2], i.e. the [] operator is applied twice in succession starting with the variable foo. There is no [][] operator to be overloaded.

    0 讨论(0)
提交回复
热议问题