如何重新赋值Eigen::Ref以切换矩阵视图?
问题
我有一个动态的Eigen::MatrixXi,想结合Eigen::Ref和Eigen::Block创建不同的视图——用同一个变量指向矩阵的不同部分,且不拷贝元素。示例代码如下:
#include <iostream> #include <Eigen/Dense> int main() { Eigen::MatrixXi m(4, 4); m << 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16; std::cout << m << "\n\n"; Eigen::Ref<Eigen::MatrixXi> ref_m = Eigen::Block<Eigen::MatrixXi>(m, 0, 0, 2, 2); std::cout << ref_m.rows() << "x" << ref_m.cols() << "\n"; std::cout << ref_m << "\n\n"; ref_m = Eigen::Block<Eigen::MatrixXi>(m, 0, 0, 3, 3); std::cout << ref_m.rows() << "x" << ref_m.cols() << "\n"; std::cout << ref_m << "\n\n"; }
问题在于:首次赋值后,ref_m指向矩阵的2x2块;但尝试重新赋值为3x3块时,ref_m并未指向新块,仍指向初始的2x2块。
想请教:
- 如何为
Eigen::Ref重新赋值? - 若无法实现,为何赋值运算符被允许?
- 有没有无需额外拷贝的实现方式?(将类型改为
Eigen::MatrixXi会拷贝块,不符合需求)
解答
1. 为什么Eigen::Ref无法重新绑定到新视图
Eigen::Ref本质是非拥有型的内存包装器,它的底层存储参数(行数、列数、内存步长等)在构造时就完全固定了。后续的赋值操作只是把源数据的内容拷贝到ref_m原本指向的内存区域,而不是改变它绑定的目标。
你代码里的ref_m = Eigen::Block<...>实际执行的是元素拷贝:把3x3块的前2x2元素复制到原来的2x2块区域,超出的部分被直接截断,所以看起来ref_m还是指向最初的2x2块。
2. 赋值运算符存在的意义
Eigen::Ref的赋值运算符是用来修改绑定区域的元素值,而非重新绑定到新内存。比如你可以用ref_m = Eigen::MatrixXi::Ones(2,2)把原来的2x2块全部设为1,这是合法的元素赋值操作,和你理解的"重新绑定"完全是两回事。
3. 无需拷贝的替代方案
如果想用同一个变量切换指向不同的矩阵块(无拷贝),有两种简单可行的方式:
方式一:直接使用Eigen::Block变量
Eigen::Block本身就是视图类型,支持直接重新赋值绑定新的块,完全无拷贝:
#include <iostream> #include <Eigen/Dense> int main() { Eigen::MatrixXi m(4, 4); m << 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16; std::cout << m << "\n\n"; Eigen::Block<Eigen::MatrixXi> block_m = m.block(0, 0, 2, 2); std::cout << block_m.rows() << "x" << block_m.cols() << "\n"; std::cout << block_m << "\n\n"; // 直接重新绑定到3x3块 block_m = m.block(0, 0, 3, 3); std::cout << block_m.rows() << "x" << block_m.cols() << "\n"; std::cout << block_m << "\n\n"; }
方式二:用std::variant存储多类型视图(C++17+)
如果需要兼容多种视图类型(比如同时用Block和Ref),可以用std::variant来存储,切换时直接赋值即可:
#include <iostream> #include <Eigen/Dense> #include <variant> int main() { Eigen::MatrixXi m(4, 4); m << 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16; std::cout << m << "\n\n"; std::variant<Eigen::Block<Eigen::MatrixXi>, Eigen::Ref<Eigen::MatrixXi>> view; view = m.block(0, 0, 2, 2); // 访问视图内容 std::visit([](auto&& v) { std::cout << v.rows() << "x" << v.cols() << "\n"; std::cout << v << "\n\n"; }, view); // 切换到3x3块 view = m.block(0, 0, 3, 3); std::visit([](auto&& v) { std::cout << v.rows() << "x" << v.cols() << "\n"; std::cout << v << "\n\n"; }, view); }
总结
Eigen::Ref从设计上就不支持重新绑定,只能修改它最初绑定区域的元素;- 赋值运算符是为元素赋值设计的,不是用来切换绑定目标;
- 直接使用
Eigen::Block变量是最简单的无拷贝切换视图的方案。
内容的提问来源于stack exchange,提问作者vsoftco
相关产品推荐
相关产品推荐

