如何高效传递Eigen::MatrixXd块引用至C++函数
问题描述
我基于Eigen库实现了一个C++函数,签名如下:
myfunc(const Eigen::ArrayXd& D, const Eigen::ArrayXd& z, Eigen::VectorXd& L, Eigen::MatrixXd& Q)
该函数接收输入数组D、z,输出VectorXd类型的L和MatrixXd类型的Q(二者会被计算结果覆盖)。上层函数处理大型数组与矩阵,仅需传递矩阵/数组的部分块,但当前只能通过拷贝块的方式调用,效率极低:
// 因数组和矩阵体积大,此方式效率极低! Eigen::ArrayXd D1 = D_part.head(nnz); Eigen::ArrayXd z1 = z_part.head(nnz); Eigen::VectorXd L1 = L.head(nnz); Eigen::MatrixXd Q1 = Q.topLeftCorner(nnz, nnz); myfunc(D1, z1, L1, Q1); D_part.head(nnz) = D1; z_part.head(nnz) = z1; L.head(nnz) = L1; Q.topLeftCorner(nnz, nnz) = Q1;
我期望无需拷贝直接传递块:
// 无需创建拷贝! myfunc(D_part.head(nnz), z_part.head(nnz), L.head(nnz), Q.topLeftCorner(nnz,nnz));
尝试使用Eigen::Ref类但未成功,最小复现示例如下:
#include <Eigen/Dense> #include <iostream> int myfunc(Eigen::Ref<Eigen::MatrixXd>& M){ M = Eigen::MatrixXd::Ones(M.rows(), M.cols()); return 0; } int main(){ Eigen::MatrixXd M = Eigen::MatrixXd::Zero(10, 10); myfunc(M.topLeftCorner(5, 5)); std::cout << M; }
运行时报错:
test.cpp: In function ‘int main()’: test.cpp:11:27: error: cannot bind non-const lvalue reference of type ‘Eigen::Ref<Eigen::Matrix<double, -1, -1> >&’ to an rvalue of type ‘Eigen::Ref<Eigen::Matrix<double, -1, -1> >’ 11 | myfunc(M.topLeftCorner(5, 5)); | ~~~~~~~~~~~~~~~^~~~~~ In file included from /home/mferrari/spack/opt/spack/linux-rocky8-zen2/gcc-11.2.0/eigen-3.4.0-xvepptisxttixdb3wrhpvzngce2xfeir/include/eigen3/Eigen/Core:308, from /home/mferrari/spack/opt/spack/linux-rocky8-zen2/gcc-11.2.0/eigen-3.4.0-xvepptisxttixdb3wrhpvzngce2xfeir/include/eigen3/Eigen/Dense:1, from test.cpp:1: /home/mferrari/spack/opt/spack/linux-rocky8-zen2/gcc-11.2.0/eigen-3.4.0-xvepptisxttixdb3wrhpvzngce2xfeir/include/eigen3/Eigen/src/Core/Ref.h:307:30: note: after user-defined conversion: ‘Eigen::Ref<PlainObjectType, Options, StrideType>::Ref(const Eigen::DenseBase<OtherDerived>&, typename Eigen::internal::enable_if<(bool)(typename Eigen::internal::traits<Eigen::Ref<PlainObjectType, RefOptions, StrideType> >::match<Derived>::MatchAtCompileTime), Derived>::type*) [with Derived = Eigen::Block<Eigen::Matrix<double, -1, -1>, -1, -1, false>; PlainObjectType = Eigen::Matrix<double, -1, -1>; int Options = 0; StrideType = Eigen::OuterStride<>; typename Eigen::internal::enable_if<(bool)(typename Eigen::internal::traits<Eigen::Ref<PlainObjectType, RefOptions, StrideType> >::match<Derived>::MatchAtCompileTime), Derived>::type = Eigen::Block<Eigen::Matrix<double, -1, -1>, -1, -1, false>; typename Eigen::internal::traits<Eigen::Ref<PlainObjectType, RefOptions, StrideType> >::match<Derived> = Eigen::internal::traits<Eigen::Ref<Eigen::Matrix<double, -1, -1> > >::match<Eigen::Block<Eigen::Matrix<double, -1, -1>, -1, -1, false> >]’ 307 | EIGEN_DEVICE_FUNC inline Ref(const DenseBase<Derived>& expr, | ^~~ test.cpp:5:41: note: initializing argument 1 of ‘int myfunc(Eigen::Ref<Eigen::Matrix<double, -1, -1> >&)’ 5 | int myfunc(Eigen::Ref<Eigen::MatrixXd>& M){ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
已知topLeftCorner返回右值,请问是否存在无需拷贝即可传递矩阵块至函数的方法?
解决方案
要实现无拷贝传递Eigen矩阵/数组块,只需调整函数参数的Eigen::Ref声明方式,核心是将参数声明为值类型的Eigen::Ref,而非非const左值引用。
原因分析
报错根源是:topLeftCorner返回的Block对象是右值,无法绑定到Eigen::Ref<T>&(非const左值引用)。而Eigen::Ref本身支持从右值表达式隐式构造,将参数声明为Eigen::Ref<T>(值传递)即可让编译器自动完成转换,且不会产生底层数据拷贝——Ref本质是对原内存块的视图。
修改步骤
- 输入参数处理:对于只读输入(如
D、z),使用const Eigen::Ref<const Eigen::ArrayXd>替代原const Eigen::ArrayXd&,兼容完整数组和各类数组块的右值表达式。 - 输出参数处理:对于可写输出(如
L、Q),使用Eigen::Ref<Eigen::VectorXd>和Eigen::Ref<Eigen::MatrixXd>替代原引用类型,允许绑定右值形式的矩阵块。
修改后代码示例
调整后的myfunc签名
void myfunc(const Eigen::Ref<const Eigen::ArrayXd>& D, const Eigen::Ref<const Eigen::ArrayXd>& z, Eigen::Ref<Eigen::VectorXd> L, Eigen::Ref<Eigen::MatrixXd> Q) { // 原函数逻辑保持不变,例如: L.setZero(); Q.setIdentity(); // ... 其他计算逻辑 }
无拷贝调用方式
// 直接传递块,无需创建拷贝 myfunc(D_part.head(nnz), z_part.head(nnz), L.head(nnz), Q.topLeftCorner(nnz, nnz));
最小复现示例修正版
#include <Eigen/Dense> #include <iostream> int myfunc(Eigen::Ref<Eigen::MatrixXd> M){ // 去掉&,改为值类型的Ref M = Eigen::MatrixXd::Ones(M.rows(), M.cols()); return 0; } int main(){ Eigen::MatrixXd M = Eigen::MatrixXd::Zero(10, 10); myfunc(M.topLeftCorner(5, 5)); // 直接传递块 std::cout << M; }
运行此修正版后,M的左上角5x5块会被设为全1,其余部分保持0,全程无内存拷贝。
额外说明
Eigen::Ref作为参数值传递时,不会拷贝底层数据,它是轻量级视图对象,仅存储原数据指针和维度信息。- 输入参数使用
const Eigen::Ref<const T>是最佳实践:既保证只读,又兼容完整矩阵/数组和各种块表达式。
内容的提问来源于stack exchange,提问作者Marcel Ferrari
相关产品推荐
相关产品推荐

