【问题标题】:2D matrix and overloading operator() / ugly syntax2D 矩阵和重载 operator() / 丑陋的语法
【发布时间】:2010-10-15 11:15:12
【问题描述】:

我在我的一个项目中使用了二维矩阵。就像C++ FAQ Lite 建议的那样。

巧妙的是,您可以像这样使用它:

int main()
{
  Matrix m(10,10);
  m(5,8) = 106.15;
  std::cout << m(5,8);
  ...
}

现在,我有一个由顶点组成的图,每个顶点都有一个指向 2D 矩阵的公共指针(只是为了示例的简单),如上所示。现在我确实有一个非常难看的语法来访问它。

(*sampleVertex.some2DTable)(0,0) = 0; //bad
sampleVertex.some2DTable->operator()(0,0) = 0; //even worse...

可能由于我对运算符重载缺乏经验,我在这里遗漏了一些语法糖。有没有更好的解决方案?

【问题讨论】:

    标签: c++ matrix operator-overloading syntactic-sugar


    【解决方案1】:

    这是不更改代码的最佳方式:

    //some2DTable is a pointer to a matrix
    (*sampleVertex.some2DTable)(0,0)
    

    您也可以改为将 some2DTable 设为对矩阵的引用,而不是指向矩阵的指针。然后你会像在你的第一个代码片段中那样简化语法。

    //some2DTable is a reference to a matrix instead of a pointer to a matrix
    sampleVertex.some2DTable(0,0)
    

    或者您可以保留 some2DTable 指向引用的指针,并简单地将引用变量存储到它并在代码块的上下文中使用它。

    【讨论】:

      【解决方案2】:
      1. 考虑使用引用而不是指针(前提是不能为空,可以在构造函数中初始化)。
      2. 考虑为返回二维矩阵引用的顶点创建一个 getter 或矩阵包装类的实例(前提是,它不能为空)。

        sampleVertex.some2DTable()(0,0) = 0;
        sampleVertex.some2DTableWrap(0,0) = 0;
        

      但是,对我来说,证明经历所有麻烦似乎不是问题。

      【讨论】:

        【解决方案3】:

        您基本上仅限于(*sampleVertex.some2DTable)(0,0)。当然,如果您不需要重新定位,为什么不将实际值存储在矩阵中呢?

        或者,将指针设为私有并创建一个访问器(注意:以下示例假设一个 EntryTypes 矩阵):

        Matrix& Vertex::GetTableRef() 
        {
            return *some2DTable; 
        }
        // or
        Matrix::EntryType& Vertex::GetTableEntry(int row, int col)
        {
            return (*some2DTable)(row,col);
        }
        
        // way later...
        myVertex.GetTableRef()(0,0) = 0;
        // or...
        myVertex.GetTableEntry(0,0) = 0;
        

        或者,如果您无法更改 Vertex 类,只需定义一个内联函数来为您执行此操作:

        // in some header file
        inline Matrix& GetTableRef(Vertex& v)
        {
            return *v.some2DTable;
        }
        
        // or you could do this
        inline Matrix::EntryType& GetTableEntry(Vertex& v, int row, int col)
        {
            return (*v.some2DTable)(row, col);
        }
        
        
        // later...
        GetTableRef(myVertex)(0, 0) = 0;
        // or
        GetTableEntry(myVertex, 0, 0) = 0;
        

        最后,不要忘记您没有有使用运算符重载。 STL 集合实现了一个 at() 成员函数,它被选中,而不是 operator[] 未被选中。如果您不介意边界检查的开销,或者您只是想成为非标准的,您可以实现 at(),然后调用 myVertex.some2DTable-&gt;at(0,0),完全省去一些语法上的麻烦。

        【讨论】:

          【解决方案4】:

          如果你有一个指向矩阵的指针,例如作为无法引用的函数参数(例如遗留代码),您仍然可以对其进行引用(伪代码):

          struct Matrix {
                  void operator () (int u, int v) {
                  }
          };
          int main () {
                  Matrix *m;
                  Matrix &r = *m;
                  r (1,1);
          }
          

          【讨论】:

            【解决方案5】:

            没有任何 C++ 语法糖可以减轻您所描述的痛苦:

            (*sampleVertex.some2DTable)(0,0) = 0; //bad
            sampleVertex.some2DTable->operator()(0,0) = 0; //even worse...
            

            在这种情况下,我要么让图形返回引用而不是指针,要么让矩阵定义一个调用 operator() 的函数:

            inline matrixType &Matrix::get( int x, int y ){ return operator()(x,y); }
            

            那么,对于顶点示例来说,语法并不那么难看:

            sampleVertex.some2DTable->get(0,0) = 0;
            

            【讨论】:

              【解决方案6】:

              我会添加一个函数,它会像 rlbond 推荐的那样返回一个 ref。为了快速修复或者如果你无法控制它的来源,我会选择这个:

              sampleVertex.some2DTable[0](0,0) = 0; // more readable
              

              这实际上是等价的,因为如果a 是指向已定义类的指针,则以下内容成立:

              *a == *(a + 0) == a[0]
              

              请参阅this comp.lang.c++ 上关于相同问题的长时间讨论,并提供很好的答案。

              【讨论】:

                【解决方案7】:

                我不知道这是否值得,但你可以这样做:

                class MatrixAccessor {
                private:
                  Matrix2D* m_Matrix;
                public:
                  MatrixAccessor(Matrix2D* matrix) : m_matrix(matrix) { }
                  double& operator()(int i, int j) const { return (*m_Matrix)(i,j); }
                  Matrix2D* operator->() const { return m_Matrix; }
                  void operator=(Matrix2D* matrix) { m_Matrix = matrix; }
                };
                

                如果原始 operator() 返回一个引用(就像在许多矩阵类中一样)。

                然后你在你的顶点类中提供那个 MatrixAccessor:

                class Vertex {
                  Matrix2D* myMatrix;
                
                public:
                  MatrixAccessor matrix;
                  Vertex(Matrix2D *theMatrix) : myMatrix(theMatrix), matrix(theMatrix) { }
                };
                

                然后你可以写:

                Vertex v;
                v.matrix(1,0) = 13;
                v.matrix->SomeOtherMatrixOperation();
                

                编辑

                我添加了const 关键字(感谢@phresnel 提出了这个主题),以便使解决方案在语义上等同于仅提供公共Matrix2D 指针的解决方案。

                此解决方案的一个优点是,可以通过添加两个非const 版本的operator()() 和operator-&gt;() 将常量转移到矩阵对象(即不能在const 顶点上修改矩阵)和将const 更改为分别返回const double&amp; 和const Matrix2D*。

                在使用指向矩阵对象的公共指针时这是不可能的。

                【讨论】:

                • 我知道它与答案并不严格相关,但您确实应该在 Vertex 析构函数中删除 myMatrix
                • PS:+1 优雅的解决方案
                • @eJames:谢谢,你是对的。相反,我从构造函数中删除了 Matrix 的分配。毕竟,如果 Vertex 负责创建和销毁,那么使用指针就没有意义了……
                • 我不想这么说,但在 const 正确的环境中,该代码几乎是无用的。
                • @phresnel:它需要一些调整,但我认为与仅使用指针的“原始”解决方案相比没有太大区别:如果 Vertex 类只有一个指向Matrix2D,常量也不会应用于矩阵。 “大部分没用”在我看来有点苛刻......
                【解决方案8】:

                我会改变你获取“sampleVertex.some2DTable”的方式,让它返回一个引用。

                要么自己创建引用,要么自己创建引用:

                Matrix& m = *sampleVertex.some2DTable;
                m(1,2) = 3;
                

                【讨论】:

                  【解决方案9】:

                  您可以通过调用成员函数来实现 Matrix::operator (int,int),并在处理指针时直接使用该成员函数。

                  class Matrix
                  {
                  public:
                    float ElementAt( int i, int j ) const { /*implement me*/ }
                    float operator() ( int i, int j ) const { return ElementAt( i, j ); }
                    ...
                  };
                  
                  void Foo(const Matix* const p)
                  {
                    float value = p->ElementAt( i, j );
                    ...
                  }
                  
                  void Bar(const Matrix& m)
                  {
                    float value = m(i,j);
                  }
                  

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2018-06-11
                    • 2011-01-16
                    • 2016-05-11
                    • 1970-01-01
                    相关资源
                    最近更新 更多