Eigen::Transpose<const Matrix3>的正确使用方法?
Correct way to use Eigen::Transpose<const Matrix3>?
我有一个旋转 class,它可以使用四元数或旋转矩阵来表示旋转。当转置函数定义如下时,我没有问题:
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 class。它保持对矩阵的引用并在反转索引的同时中继访问元素的调用。这个 class 的目标是能够使用转置矩阵而不必实际复制矩阵本身。
换句话说,这是 Transpose
class 定义的一个非常简化的版本:
struct Transpose<const Matrix3> {
const Matrix3& m_matrix;
const float& coeffRef(int row, int col) const {
return m_matrix.coeffRef(col,row);
}
}
您可以看到实际来源here。
关键部分是 Transpose
class 将 reference 存储到给定矩阵。
现在 transpose
函数的第二个版本发生了什么?
- 您使用
quaternion.toRotationMatrix().eval()
创建一个临时 Matrix3
- 然后您围绕这个临时矩阵创建一个
Transpose
包装器,存储对该矩阵的引用。
当您从函数 return 中 Transpose
时,您 return 引用了一个超出范围的对象。这称为 ,它会导致未定义的行为(您看到 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;
}
请注意,这在第一个版本中不会发生,因为您的 return 类型是 Matrix3
。在这种情况下,您创建的 Transpose
对象将转换为要进行 returned 的新 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
作为你的 return 类型(也许你 真的 想避免复制操作),你需要将旋转矩阵存储在永久位置(例如,作为 class 中的缓存 Matrix3
成员变量)以避免悬空引用问题。
另一种选择是传递一个预先存在的矩阵进行填充,例如
void Rot3::setToTranspose() (Matrix3& result)
{
result = quaternion_.toRotationMatrix().transpose();
}
我有一个旋转 class,它可以使用四元数或旋转矩阵来表示旋转。当转置函数定义如下时,我没有问题:
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 class。它保持对矩阵的引用并在反转索引的同时中继访问元素的调用。这个 class 的目标是能够使用转置矩阵而不必实际复制矩阵本身。
换句话说,这是 Transpose
class 定义的一个非常简化的版本:
struct Transpose<const Matrix3> {
const Matrix3& m_matrix;
const float& coeffRef(int row, int col) const {
return m_matrix.coeffRef(col,row);
}
}
您可以看到实际来源here。
关键部分是 Transpose
class 将 reference 存储到给定矩阵。
现在 transpose
函数的第二个版本发生了什么?
- 您使用
quaternion.toRotationMatrix().eval()
创建一个临时 - 然后您围绕这个临时矩阵创建一个
Transpose
包装器,存储对该矩阵的引用。
Matrix3
当您从函数 return 中 Transpose
时,您 return 引用了一个超出范围的对象。这称为 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;
}
请注意,这在第一个版本中不会发生,因为您的 return 类型是 Matrix3
。在这种情况下,您创建的 Transpose
对象将转换为要进行 returned 的新 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
作为你的 return 类型(也许你 真的 想避免复制操作),你需要将旋转矩阵存储在永久位置(例如,作为 class 中的缓存 Matrix3
成员变量)以避免悬空引用问题。
另一种选择是传递一个预先存在的矩阵进行填充,例如
void Rot3::setToTranspose() (Matrix3& result)
{
result = quaternion_.toRotationMatrix().transpose();
}