Eigen3如何实现稠密矩阵与稀疏矩阵的多态操作
问题解答
Eigen 没有内置可同时兼容稠密、稀疏矩阵的公共多态基类可直接作为UnknownType使用,你可以通过手写轻量类型擦除的抽象接口实现需求,全程不会拷贝大型矩阵数据,也不需要分配大块内存。
实现方案
- 定义一个仅包含所需操作接口的轻量抽象基类,不持有任何矩阵数据:
#include <Eigen/Dense> #include <Eigen/Sparse> #include <vector> struct IMatrixAddable { // 接口:将当前矩阵累加到传入的稠密结果矩阵上 virtual void add_to(Eigen::MatrixXd& result) const = 0; virtual ~IMatrixAddable() = default; };
- 针对稠密、稀疏矩阵分别实现极薄的包装类,内部仅存储原矩阵的指针,零数据拷贝:
// 稠密矩阵包装 struct DenseMatWrapper final : IMatrixAddable { const Eigen::MatrixXd* mat_ptr; explicit DenseMatWrapper(const Eigen::MatrixXd* p) : mat_ptr(p) {} void add_to(Eigen::MatrixXd& result) const override { result += *mat_ptr; } }; // 稀疏矩阵包装 struct SparseMatWrapper final : IMatrixAddable { const Eigen::SparseMatrix<double>* mat_ptr; explicit SparseMatWrapper(const Eigen::SparseMatrix<double>* p) : mat_ptr(p) {} void add_to(Eigen::MatrixXd& result) const override { result += *mat_ptr; } };
- 替换你代码中的
UnknownType为上述抽象接口即可,注意保证包装对象的生命周期覆盖matrices的使用周期,也可以用智能指针管理包装对象避免生命周期问题:
std::vector<const IMatrixAddable*> matrices; // 初始化包装,仅存指针,不拷贝原矩阵 DenseMatWrapper dense_wrap(get_large_dense_matrix_ptr()); SparseMatWrapper sparse_wrap(get_large_sparse_matrix_ptr()); matrices.emplace_back(&dense_wrap); matrices.emplace_back(&sparse_wrap); Eigen::MatrixXd result(known_size, known_size); result.setZero(); // 必须初始化,否则结果会包含随机垃圾值 for (const auto* mat : matrices) { mat->add_to(result); }
方案说明
- 该方案的额外开销仅为每个包装类单指针大小的内存,以及循环中每次一次虚函数调用,性能损耗可以忽略。
- 全程不会复制或移动原始大型矩阵数据,完全符合避免大块内存分配/拷贝的要求。
- 不要尝试直接使用Eigen内部的
EigenBase等模板基类做动态多态,这类基类是为CRTP静态多态设计的,没有可用的虚函数接口,强行使用会触发编译错误或未定义行为。 - 不要通过
void*存储指针再手动做类型分支判断,这种写法属于未定义行为,可维护性极差。
内容的提问来源于stack exchange,提问作者Miguelito Valdez
相关产品推荐
相关产品推荐

