c++ 11不能强制转换模板继承

C++11 Cannot cast template inheritance

本文关键字:继承 转换 不能 c++      更新时间:2023-10-16

我正在为一个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'

这是因为乘法运算返回的是MatrixN<3>而不是Matrix3

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


代码(未测试):

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

问题是operator*的结果类型是MatrixN<N>,但rotation的类型是Matrix3,并且隐式强制转换不起作用,因为这将是向下强制转换。

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

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函数的"用户"应该小心。

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

你可以定义你的乘法运算符接受任何类型作为参数并返回该类型,然后约束它只接受从MatrixN<N>派生的类型:

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

这段代码可以工作。但是你应该知道这是非常非常危险的。

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<N>operator *返回的数据。因此,内存将在超出作用域时被释放。这段代码告诉编译器在MatrixN<N>变量上强制使用Matrix3的方法。Matrix3MatrixN<N>的内存布局是相同的,否则可能会导致程序段故障而崩溃。


根据您的代码,您可能希望在模板参数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);
};