You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Eigen中auto存储四元数惰性运算表达式调用eval时访问临时对象崩溃

Eigen四元数运算表达式模板引发悬空内存访问崩溃

问题复现代码

const Eigen::Vector3d position = m_rigidBody->pos().segment<3>(0); // pos() 返回 const Eigen::VectorXd& 类型
const Eigen::Quaterniond &rotation = m_rigidBody->rot();

const auto p = rotation * m_r + position; // 使用auto接收惰性求值表达式

const auto skew = [](const Eigen::Vector3d &v) -> Eigen::Matrix3d {
    return Eigen::Matrix3d{{0, -v[2], v[1]},
                        {v[2], 0, -v[0]},
                        {-v[1], v[0], 0}};
};

MapType j = MapType::Zero();
j.block<3, 3>(0, 0).setIdentity();
j.block<3, 3>(0, 3) = skew((-(p - position)).eval()); // 此处触发崩溃,报非法内存访问错误
return j;

问题根因

  • 崩溃的核心原因是跨语句使用auto持有Eigen几何模块的惰性求值表达式时,表达式树内部出现了悬空引用,你之前认为所有依赖值都存活的判断,对四元数相关的表达式不成立:
    • 用const auto接收运算结果时不会触发Eigen的即时求值,p本质是嵌套的表达式模板对象,不是存储实际数值的Eigen::Vector3d,整个表达式树默认按引用绑定依赖的操作数。
    • Eigen 3.4之前的版本中,四元数乘向量返回的QuaternionRotateVectorReturnType存在实现缺陷:当这个旋转表达式作为子节点被更长的链式运算表达式(比如你写的rotation * m_r + position)拷贝存储时,内部持有的四元数引用会意外悬空,后续调用eval()触发整棵表达式树求值时,就会访问到已经无效的内存地址。
    • 普通矩阵乘向量返回的表达式没有这个实现缺陷,因此把四元数乘法换成旋转矩阵乘法后,代码可以正常运行。

修复方法

  • 最稳妥的方案:不要用auto跨语句接收Eigen几何模块(四元数、旋转变换、位姿变换等)的运算结果,直接显式声明值类型触发即时求值,把运算结果落地为实际的数值对象即可:
    // 显式指定类型为Vector3d,此处直接完成求值,p存储实际计算得到的向量值
    const Eigen::Vector3d p = rotation * m_r + position;
    
  • 修改后原有(-(p - position)).eval()的逻辑不需要任何调整,就能正常运行。
  • 即使用了修复该bug的高版本Eigen,也不建议跨语句用auto持有Eigen表达式模板:表达式模板的设计初衷是优化单条链式语句内的临时运算效率,不是用来做跨语句的变量存储,随意使用很容易踩生命周期的坑。

内容的提问来源于stack exchange,提问作者jjcasmar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 02:30:54