#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*
что возвращает aMatrixN<N>
, а не aMatrix3
.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);
};