如何通过SparseSolverBase基类指针调用Eigen库任意类型稀疏线性求解器
错误原因
Eigen 的 SparseSolverBase 是 CRTP(奇异递归模板模式)实现的静态多态基类,它的模板参数是派生求解器类型本身,而非矩阵类型。比如 Eigen::SimplicialLLT<Eigen::SparseMatrix<double>> 实际继承的是 SparseSolverBase<Eigen::SimplicialLLT<Eigen::SparseMatrix<double>>>, 和你声明的 Eigen::SparseSolverBase<Eigen::SparseMatrix<double>> 是完全不同的两个类型,自然无法直接做指针转换。
实现方案
通过手动实现类型擦除层,构造运行时多态接口适配所有Eigen稀疏求解器,步骤如下:
- 定义抽象求解器接口基类,声明所有求解器需要的公共方法(
compute、solve等) - 编写模板派生类,适配任意Eigen稀疏求解器,转发调用对应求解器的实现
LinearSolver类中存储抽象基类的智能指针,根据枚举值实例化对应模板派生类对象
修改后完整可运行代码
#pragma once #include <Eigen/SparseCore> #include <Eigen/SparseCholesky> #include <Eigen/SparseQR> #include <Eigen/SparseLU> #include <Eigen/IterativeLinearSolvers> #include <iostream> #include <unsupported/Eigen/SparseExtra> #include <memory> enum solverType { SimplicialLLT, SimplicialLDLT, SparseLU, ConjugateGradient, LeastSquaresConjugateGradient, BiCGSTAB }; // 抽象求解器接口 class SolverInterface { public: virtual ~SolverInterface() = default; virtual void compute(const Eigen::SparseMatrix<double>& A) = 0; virtual Eigen::VectorXd solve(const Eigen::VectorXd& b) const = 0; // 可按需补充更多接口,比如获取迭代次数、计算状态、设置公差等 }; // 模板派生类,适配任意Eigen稀疏求解器 template<typename SolverType> class SolverImpl : public SolverInterface { private: SolverType m_solver; public: void compute(const Eigen::SparseMatrix<double>& A) override { m_solver.compute(A); } Eigen::VectorXd solve(const Eigen::VectorXd& b) const override { return m_solver.solve(b); } // 转发基类声明的其他接口 }; class LinearSolver { public: LinearSolver(Eigen::SparseMatrix<double> &A, Eigen::VectorXd &b, solverType type) : A(A), b(b) { switch (type) { case SimplicialLLT: solver = std::make_unique<SolverImpl<Eigen::SimplicialLLT<Eigen::SparseMatrix<double>>>>(); break; case SimplicialLDLT: solver = std::make_unique<SolverImpl<Eigen::SimplicialLDLT<Eigen::SparseMatrix<double>>>>(); break; case SparseLU: solver = std::make_unique<SolverImpl<Eigen::SparseLU<Eigen::SparseMatrix<double>>>>(); break; case ConjugateGradient: solver = std::make_unique<SolverImpl<Eigen::ConjugateGradient<Eigen::SparseMatrix<double>>>>(); break; case LeastSquaresConjugateGradient: solver = std::make_unique<SolverImpl<Eigen::LeastSquaresConjugateGradient<Eigen::SparseMatrix<double>>>>(); break; case BiCGSTAB: solver = std::make_unique<SolverImpl<Eigen::BiCGSTAB<Eigen::SparseMatrix<double>>>>(); break; } solver->compute(A); }; ~LinearSolver() = default; // 对外暴露求解方法 Eigen::VectorXd solve() const { return solver->solve(b); } private: Eigen::SparseMatrix<double> A; Eigen::VectorXd b; std::unique_ptr<SolverInterface> solver; };
额外注意事项
- 原代码的switch语句未加
break,会导致匹配到对应case后仍执行所有后续case的赋值逻辑,必须补充break避免bug - 用
std::unique_ptr管理内存,无需手动释放,析构时自动调用对应资源的释放逻辑 - 如果需要支持更多求解器的特殊配置,只需要在
SolverInterface中补充虚方法声明,在SolverImpl中转发调用即可
内容的提问来源于stack exchange,提问作者ElPotac
相关产品推荐
相关产品推荐

