【问题标题】:C++11 Cannot cast template inheritanceC++11 不能转换模板继承
【发布时间】:2016-10-08 09:34:58
【问题描述】:

我正在为一个 OpenGLES 项目编写数学模块。 我写了一个类来管理通用大小的浮点矩阵

template <unsigned int N>
class MatrixN {

    public:
        float m[N*N];

        MatrixN(){}
        virtual ~MatrixN(){}

        void identify();

        MatrixN& operator=(const MatrixN& b);
};

template <unsigned int N>
MatrixN<N> operator*(const MatrixN<N>& a, const MatrixN<N>& b);


//CPP file implementation
template<unsigned int N>
MatrixN<N> operator*(const MatrixN<N>&a, const MatrixN<N> &b) {
    MatrixN<N> matrix;

    for(unsigned int i = 0; i < N; i++){
        for(unsigned int j = 0; j < N; j++){
            matrix.m[i * N + j] = 0;
            for(unsigned int z = 0; z < N; z++){
                matrix.m[i * N + j] += a.m[i * N + z] * b.m[z * N + j];
            }
        }
    }

    return matrix;
}

我创建了一个子类来管理 3x3 矩阵

class Matrix3 : public MatrixN<3> {

    public:
        void rotateX(const float radians);
        void rotateY(const float radians);
        void rotateZ(const float radians);
};

为什么当我执行这个操作时

//Rotations instances of Matrix3
Matrix3 rotation = this->rotation * input.rotation;

我在编译时收到此错误?

no viable conversion from 'MatrixN<3U>' to 'const Matrix3'

【问题讨论】:

  • 因为operator* 返回的是MatrixN&lt;N&gt;,而不是Matrix3
  • input.rotation 是 MatrixN 的类型?

标签: c++ c++11 math matrix


【解决方案1】:

这是因为乘法运算返回MatrixN&lt;3&gt;而不是Matrix3

在这种情况下,您可以在Matrix3 中创建一个接受MatrixN&lt;3&gt; 的构造函数


代码(未测试):

class Matrix3 : public MatrixN<3> {

    public:
        Matrix3 (const MatrixN<3>& mat){/*set internal values*/}
        void rotateX(const float radians);
        void rotateY(const float radians);
        void rotateZ(const float radians);
};

【讨论】:

    【解决方案2】:

    问题是 operator* 的结果类型是 MatrixN&lt;N&gt;rotation 的类型是 Matrix3 并且隐式转换不起作用,因为这将是向下转换。

    作为一种可能的解决方案,您可以为Matrix3 输入和输出覆盖operator*。使用辅助函数,如果您想重用它,您可以节省一些代码。例如:

    template< MatrixType >
    MatrixType multiple(const MatrixType& a, const MatrixType& b, size_t N )
    {
        MatrixType matrix;
    
        for(unsigned int i = 0; i < N; i++)
        {
            for(unsigned int j = 0; j < N; j++)
            {
                matrix.m[i * N + j] = 0;
                for(unsigned int z = 0; z < N; z++)
                {
                    matrix.m[i * N + j] += a.m[i * N + z] * b.m[z * N + j];
                }
            }
        }
    
        return matrix;
    }
    
    Matrix3 operator*(const Matrix3& a, const Matrix3& b)
    {
        return multiple< Matrix3 >( a, b, 3 );
    }
    

    请注意,这可能有点危险,因为没有任何保护措施可以避免溢出,因此multiple 函数的“用户”应该小心。

    【讨论】:

      【解决方案3】:

      您的乘法运算符是根据MatrixN&lt;N&gt; 实现的。您的派生类型 Matrix3 不是其中之一,而是其中之一作为基础。所以仍然有一个派生到基的转换来调用操作符,并且返回的类型不是你想要的类型。

      您可以将乘法运算符定义为采用任何类型作为参数并返回此类型,然后将其限制为仅采用派生自 MatrixN&lt;N&gt; 的类型:

      template<typename Matrix,
               typename = std::enable_if_t<std::is_base_of<MatrixN<Matrix::size>, Matrix>::value>>
      Matrix operator*(const Matrix&a, const Matrix &b) {
          constexpr unsigned N = Matrix::size;
          Matrix matrix;
      
          for(unsigned int i = 0; i < N; i++){
              for(unsigned int j = 0; j < N; j++){
                  matrix.m[i * N + j] = 0;
                  for(unsigned int z = 0; z < N; z++){
                      matrix.m[i * N + j] += a.m[i * N + z] * b.m[z * N + j];
                  }
              }
          }
      
          return matrix;
      }
      

      要静态确定矩阵的大小N,类模板需要有一个嵌套的常量表达式值size,例如:

      template <unsigned int N>
      class MatrixN {
      
          public:
              static constexpr unsigned int size = N;
              // ...
      };
      

      【讨论】:

        【解决方案4】:

        此代码可能有效。但你应该知道这是非常非常危险的。

        MatrixN<3> tmp_scoped_var = this->rotation * input.rotation;
        Matrix3 &rotation = reinterpret_cast<Matrix3 &>(tmp_scoped_var);
        rotation.rotateX(1.1); // Call the method of B
        

        因为tmp_scoped_var 保存了MatrixN&lt;N&gt;operator * 返回的数据。因此,内存将在超出范围时被释放。这段代码告诉编译器在MatrixN&lt;N&gt; 变量上强制使用Matrix3 的方法。 Matrix3MatrixN&lt;N&gt;的内存布局相同,否则程序可能会因为段错误而崩溃。


        根据您的代码,您可能希望在模板参数N 等于3 时添加一些特定方法。所以可以使用类模板特化。

        template <>
        class MatrixN<3> {
        
            public:
                float m[3*3];
        
                MatrixN(){}
                virtual ~MatrixN(){}
        
                void identify();
        
                MatrixN& operator=(const MatrixN<3>& b);
        
            public:
                void rotateX(const float radians);
                void rotateY(const float radians);
                void rotateZ(const float radians);
        };
        

        【讨论】:

          猜你喜欢
          • 2013-05-21
          • 2015-11-10
          • 1970-01-01
          • 1970-01-01
          • 2011-04-17
          • 1970-01-01
          • 2013-09-04
          • 2020-10-14
          • 1970-01-01
          相关资源
          最近更新 更多