Reputation: 4282
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;
would set the first row, first column entry to 1.0f.
I'd like to provide similar access. How can I overload the [][] operator
s?
I've tried the following, but I get errors:
operator name must be declared as a function
const real operator[][](int row, int col) const
{
// should really throw an exception for out of bounds indices
return ((row >= 0 && row <= 2) && (col >= 0 && col <= 2)) ? _data[row][col] : 0.0f;
}
What's the correct way to overload this operator?
Upvotes: 3
Views: 1732
Reputation: 145204
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.
Upvotes: 5
Reputation: 5305
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)
.
Upvotes: 6
Reputation: 6266
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.
Upvotes: 1
Reputation: 726479
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;
}
Upvotes: 7
Reputation: 473252
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.
Upvotes: 3