Eigen::Transpose
- это просто класс обертка вокруг существующей матрицы. Он сохраняет ссылку на матрицу и передает вызов для доступа к элементам, одновременно изменяя индексы. Цель этого класса - иметь возможность использовать транспонированную матрицу без необходимости фактически копировать саму матрицу.
Другими словами, это очень упрощенная версия определения класса Transpose
:
struct Transpose<const Matrix3> {
const Matrix3& m_matrix;
const float& coeffRef(int row, int col) const {
return m_matrix.coeffRef(col,row);
}
}
Вы можете увидеть фактический источник здесь .
Критическая часть заключается в том, что класс Transpose
хранит ссылку на данную матрицу.
Что теперь происходит во второй версии функции transpose
?
- вы создаете временную
Matrix3
с помощью quaternion.toRotationMatrix().eval()
- , затем вы создаете
Transpose
обертка вокруг этой временной матрицы, хранящая ссылку на эту матрицу.
Когда вы возвращаете из функции, возвращаемая Transpose
имеет ссылку на объект, который вышел из области видимости. , Это называется свисающая ссылка , что приводит к неопределенному поведению (причина, по которой вы видите NaN
s, наиболее вероятно, что память, в которой находилась ваша временная матрица, была перезаписана другими данными). Код эквивалентен этому:
Eigen::Transpose<const Matrix3> Rot3::transpose() const {
// Equivalent to your second version
Matrix3 temp = quaternion_.toRotationMatrix().eval();
Transpose<const Matrix3> result = temp.transpose(); // Holds ref to temp -> Leads to UB
return result;
}
Обратите внимание, что это не происходит в первой версии, потому что ваш тип возврата - Matrix3
. В этом случае создаваемый вами объект Transpose
преобразуется в новый возвращаемый объект Matrix3
, который копирует коэффициенты. Вот эквивалентная версия вашей первой функции.
Matrix3 Rot3::transpose() const {
// Equivalent to your first version
Matrix3 temp = quaternion_.toRotationMatrix().eval();
Matrix3 result = temp.transpose(); // Does a full copy of the transpose of `temp`
return result;
}
Если вы по-прежнему хотите использовать Eigen::Transpose
в качестве типа возврата (возможно, вы действительно хотите избежать операции копирования) вам нужно хранить матрицу вращения в постоянном месте (например, как кешированную Matrix3
переменную-член в вашем классе), чтобы избежать этой проблемы со ссылками.
Другой вариант - пропустить уже существующую матрицу для заполнения, например,
void Rot3::setToTranspose() (Matrix3& result)
{
result = quaternion_.toRotationMatrix().transpose();
}