C 11 не может использовать наследование шаблонов

#c #c 11 #math #matrix

#c #c 11 #математика #матрица

Вопрос:

Я пишу математический модуль для проекта OpenGLES. Я написал класс для управления матрицами с плавающей запятой для общего размера

 template <unsigned int N>
class MatrixN {

    public:
        float m[N*N];

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

        void identify();

        MatrixNamp; operator=(const MatrixNamp; b);
};

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


//CPP file implementation
template<unsigned int N>
MatrixN<N> operator*(const MatrixN<N>amp;a, const MatrixN<N> amp;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;
}
  

И я создаю подкласс для управления матрицами 3×3

 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'
  

Комментарии:

1. Потому operator* что возвращает a MatrixN<N> , а не a Matrix3 .

2. input.rotation — это тип MatrixN<3> ?

Ответ №1:

Это потому, что операция умножения возвращает MatrixN<3> и не Matrix3

В этом случае вы можете создать конструктор в Matrix3 этом accept MatrixN<3>


код (не тестировался) :

 class Matrix3 : public MatrixN<3> {

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

Ответ №2:

Проблема в том, что тип результата operator* is MatrixN<N> , но тип rotation is Matrix3 и неявное приведение не работают, потому что это было бы понижением.

В качестве возможного решения вы можете перезаписать operator* Matrix3 входы и выходы for . С помощью вспомогательной функции вы можете сэкономить немного кода, если захотите использовать его повторно. Например:

 template< MatrixType >
MatrixType multiple(const MatrixTypeamp; a, const MatrixTypeamp; 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 Matrix3amp; a, const Matrix3amp; b)
{
    return multiple< Matrix3 >( a, b, 3 );
}
  

Обратите внимание, что это может быть немного опасно, потому что нет никакой защиты, чтобы избежать переполнения, поэтому «пользователь» multiple функции должен быть осторожен.

Ответ №3:

Ваш оператор умножения реализован в терминах 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 Matrixamp;a, const Matrix amp;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 amp;rotation = reinterpret_cast<Matrix3 amp;>(tmp_scoped_var);
rotation.rotateX(1.1); // Call the method of B
  

Потому tmp_scoped_var что храните данные, возвращаемые MatrixN<N> ‘s operator * . Таким образом, память будет освобождена, находясь вне области видимости. Этот код сообщает компилятору принудительно использовать Matrix3 метод ‘s для MatrixN<N> переменной. Схемы расположения памяти Matrix3 и MatrixN<N> идентичны, в противном случае программа может завершиться сбоем из-за ошибки сегмента.


Согласно вашему коду, вы можете захотеть добавить какой-то конкретный метод, пока параметр шаблона N равен 3 . Таким образом, можно использовать специализацию шаблона класса.

 template <>
class MatrixN<3> {

    public:
        float m[3*3];

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

        void identify();

        MatrixNamp; operator=(const MatrixN<3>amp; b);

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