我有一个旋转类,可以使用四元数或旋转矩阵来表示旋转。如下定义转置函数时,我没有问题:
Matrix3 Rot3::transpose() const {
// quaternion_ is a class variable of type `Quaternion`.
return quaternion_.toRotationMatrix().transpose();
}
当我使用Eigen::Transpose
切换到推荐版本时,我的单元测试失败(并且我得到了NaN)
Eigen::Transpose<const Matrix3> Rot3::transpose() const {
// quaternion_ is a class variable of type `Quaternion`.
return quaternion_.toRotationMatrix().eval().transpose();
}
我需要以这种怪异的方式使用.eval()
,否则编译器会发出模糊的错误消息。我的猜测是我对Eigen::Transpose
声明的使用与我返回的内容不一致。关于此方法为何表现如此奇怪的任何帮助或建议,以及有关此方法正确方法的任何建议?
Eigen::Transpose
只是围绕现有矩阵的wrapper类。它保留对矩阵的引用,并在反转索引的同时中继调用以访问元素。此类的目标是能够使用转置矩阵,而不必实际复制矩阵本身。
换句话说,这是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);
}
}
。
关键部分是here类存储给定矩阵的reference。
现在第二版Transpose
函数会发生什么?
transpose
创建一个临时的Matrix3
quaternion.toRotationMatrix().eval()
包装器,存储对此矩阵的引用。从函数返回时,返回的Transpose
具有对超出范围的对象的引用。这称为Transpose
,它导致未定义的行为(您看到dangling reference的原因很可能是临时矩阵所在的内存已被其他数据覆盖)。该代码等效于此:
NaN
请注意,在第一个版本中不会发生这种情况,因为您的返回类型为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;
}
作为返回类型(也许您[[really避免复制操作),则需要将旋转矩阵存储在一个永久位置(例如,作为缓存的Eigen::Transpose
]类中的成员变量),以避免出现悬而未决的引用问题。
Matrix3