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

如何高效传递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本质是对原内存块的视图。

修改步骤

  1. 输入参数处理:对于只读输入(如D、z),使用const Eigen::Ref<const Eigen::ArrayXd>替代原const Eigen::ArrayXd&,兼容完整数组和各类数组块的右值表达式。
  2. 输出参数处理:对于可写输出(如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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 00:07:01