【问题标题】:overloading [][] operators in c++在 C++ 中重载 [][] 运算符
【发布时间】:2012-09-30 00:59:19
【问题描述】:

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

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

我想提供类似的访问权限。如何使[][] operators 过载?

我尝试了以下方法,但出现错误:

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

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

重载此运算符的正确方法是什么?

【问题讨论】:

  • 如果你能处理 matrix(x,y) 语法,那就去吧。我们的代码库不能,因为我们希望能够使用二维数组快速交换遗留代码以使用我们的矩阵类。一种牺牲一些安全性但快速实现您想要的方法是: const real* operator[](int row) const { return _data[row]; };然而,这牺牲了在获取列时对结果进行边界检查的能力,因此可以使用代理对象来获得更安全的结果。
  • 我真的很希望能够将我的代码与 glm 代码互换,只需更改使用的命名空间,所以我不能使用 operator()。

标签: c++ operator-overloading


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

    创建方法double operator() (int row, int col) const 会更容易。而不是matrix[i][j],你只需说matrix(i,j)

    【讨论】:

      【解决方案3】:

      通常,对于多个参数,您要使用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[] 不同,它可以接受任意数量的参数。

      【讨论】:

        【解决方案4】:

        没有[][] 运算符。 GLM 的做法是从第一个 [] 返回一个 vec3&amp;vec3 有自己的 [] 运算符重载。所以它是两个独立的类上的两个独立的operator[]s。

        这也是 GLSL 的工作原理。第一个 [] 将列作为向量。第二个获取向量并从中获取值。

        【讨论】:

          【解决方案5】:

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

          【讨论】:

            猜你喜欢
            • 2012-10-20
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2016-02-19
            • 1970-01-01
            相关资源
            最近更新 更多