在C++中重载[][]运算符

3

我正在用C++编写一个3x3矩阵类。

glm::mat3通过[][]运算符语法提供对矩阵数据的访问。
例如,myMatrix[0][0] = 1.0f;将把第一行第一列的条目设置为1.0f

我想提供类似的访问方式。 我如何重载[][]运算符

我尝试了以下方法,但是我得到了错误:

操作符名称必须声明为函数

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;
}

如何正确地重载这个运算符?


如果你能处理矩阵(x,y)语法,就使用它。我们的代码库不能这样做,因为我们希望能够快速地交换使用二维数组的遗留代码以使用我们的矩阵类。一种牺牲了一些安全性但可以快速实现你想要的方法是:const real* operator[](int row) const { return _data[row]; };然而,这会牺牲在获取列时对结果进行边界检查的能力,因此可以使用代理对象来获得更安全的结果。 - stinky472
我真的很希望能够通过更改所使用的命名空间来交换我的代码和glm代码,以便我不能使用operator()。 - fishfood
5个回答

7

没有 [][] 运算符,因此您需要两次重载 [] 运算符:一次在矩阵上,返回一个用于行的 代理 对象,一次在返回的代理行上:

// 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;
}

6

为了更容易,可以将方法 double operator() (int row, int col) const 实现。使用 matrix(i,j) 代替 matrix[i][j]


5
通常情况下,对于多个参数,您应该使用operator()而不是operator[]
嗯,如果不明显的话,在C ++中没有operator[][]。它只是两次应用了operator[]。这意味着,如果你想要那种表示方式,那么你必须让第一个返回一个可索引的东西或代理,以便可以应用第二个。
下面的代码概述了一些方法,请选择您喜欢的方法:
#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;
}

噢,我发完那段代码后发现我没有完全隐藏Mat5的内部存储:它需要一个额外的代理层,就像Mat4一样。所以这种方法真的很复杂。我不会这么做(Mat1我认为很简单),但有些人认为代理很酷,数据隐藏更酷...

总之,没有“唯一正确”的重载operator[]的方法。有许多方法(如上面的代码所示),每种方法都有一些权衡。通常你最好使用operator(),因为与operator[]相反,它可以接受任意数量的参数。


3

在GLM中,没有[][]操作符。它的做法是从第一个[]返回一个vec3&vec3有自己的[]操作符重载。所以这是两个不同的operator[]在两个不同的类上。

GLSL也是这样工作的。第一个[]获取列向量。第二个从向量中获取值。


1

foo[1][2]表达式实际上被解释为(foo[1])[2],即[]运算符连续两次应用于变量foo。没有[][]运算符可以重载。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接