Eigen如何将矩阵行/列作为左值Vector引用传递给函数
编译报错来自Eigen的表达式模板设计机制:m.col()、m(Eigen::all, 0)、m.block()这类获取子矩阵/子视图的API,返回值不是Eigen::VectorXd或固定尺寸列向量类型的实体对象,而是Block类型的代理表达式对象。将该代理对象传入参数为Eigen::VectorXd&的函数时,编译器会尝试隐式将代理对象转换为Eigen::VectorXd类型,转换过程会生成临时右值对象,而C++语法不允许非const左值引用绑定到右值,因此触发编译错误。
注意:不要尝试用f2(m.col(0).eval())这类写法绕过编译,eval()会生成原列数据的独立拷贝,对该拷贝的修改不会作用到原矩阵上,完全无法实现引用传参修改原数据的需求。
方案1:使用Eigen::Ref修改函数参数(官方推荐,无额外开销)
Eigen官方专门提供了Ref类适配这类需要接收任意可写、内存布局兼容的向量/矩阵子视图的场景,不会发生数据拷贝,对参数的修改会直接作用到原矩阵上,动态尺寸、固定尺寸矩阵的列、行、块都可以直接传入。
动态尺寸场景修改后代码:
#include "eigen3/Eigen/Eigen" // 将参数替换为对应类型的Ref即可 void f2(Eigen::Ref<Eigen::VectorXd> v) { v(0) = 0e0; } void f1(Eigen::MatrixXd &m) { if (m(0,0) == 0e0) { f2(m.col(0)); // 直接传参即可编译通过,修改直接作用于原矩阵 f2(m(Eigen::all, 0)); // 同样可以正常编译 } } int main() { Eigen::MatrixXd m = Eigen::MatrixXd::Random(3,3); f1(m); return 0; }
固定尺寸场景改法同理,替换为对应固定尺寸类型的Ref即可:
constexpr const int N = 3; // 对应固定尺寸列向量的Ref类型 void f2(Eigen::Ref<Eigen::Matrix<double,N,1>> v) { v(0) = 0e0; } void f1(Eigen::Matrix<double,N,N> &m) { if (m(0,0) == 0e0) { f2(m.col(0)); // 正常编译 f2(m(Eigen::all, 0)); // 正常编译 f2(m.block<N,1>(0,0)); // 正常编译 } } int main() { Eigen::Matrix<double,N,N> m = Eigen::Matrix<double,N,N>::Random(); f1(m); return 0; }
该方案是Eigen官方推荐的通用写法,编写向量/矩阵操作函数时优先用Ref做参数,兼容性最好且无额外性能开销。
方案2:无法修改f2签名时,用Map构造共享内存的映射对象
如果受历史代码限制改不了f2的参数,必须接收具体的Eigen::VectorXd&或固定尺寸向量的非const引用,可以用Eigen::Map直接在原矩阵列的内存上构造向量映射对象。该对象是左值,和原矩阵共享内存,不会产生拷贝,修改它等同于修改原矩阵的对应列。
动态尺寸场景示例:
void f1(Eigen::MatrixXd &m) { if (m(0,0) == 0e0) { // 基于第0列的内存地址构造Map映射,和原矩阵共享数据 Eigen::Map<Eigen::VectorXd> col0(m.col(0).data(), m.rows()); f2(col0); // 可正常绑定引用,修改直接作用于原矩阵 } }
固定尺寸场景示例:
constexpr const int N = 3; void f1(Eigen::Matrix<double,N,N> &m) { if (m(0,0) == 0e0) { Eigen::Map<Eigen::Matrix<double,N,1>> col0(m.col(0).data()); f2(col0); // 正常编译,无数据拷贝 } }
内容的提问来源于stack exchange,提问作者xnth

