基于标量类型模板的Eigen容器适配问题求助
你的问题核心在于:当使用Ref<Matrix<T,Dynamic,Dynamic>>作为函数参数时,编译器无法从传入的Matrix<T,...>实例自动推导出模板参数T——因为Matrix和Ref是不同的类型,模板推导无法跨越这层包装。下面给你几个实用的解决方案,既能保留你基于标量T的类结构,又能完美支持Eigen的各种容器(包括Matrix、Ref、Block等)。
方案1:使用Eigen的DenseBase作为通用参数(推荐)
这是Eigen官方推荐的写法,它能接受所有Eigen稠密表达式类型,同时自动提取标量类型,不需要显式指定模板参数。修改后的sum2如下:
#include<iostream> #include<Eigen/Dense> #include<type_traits> using namespace std; using namespace Eigen; double sum1(const Ref<const Matrix<double,Dynamic,Dynamic>> &m) { double x = 0.0; for (int j=0; j<m.cols(); ++j) for (int i=0; i<m.rows(); ++i) x += m(i,j); return x; } template <typename Derived> typename Derived::Scalar sum2(const DenseBase<Derived>& m) { using T = typename Derived::Scalar; T x = T(0); // 用T的默认构造,避免0.0对整数类型不兼容的问题 for (int j=0; j<m.cols(); ++j) for (int i=0; i<m.rows(); ++i) x += m(i,j); return x; } int main() { Matrix<double,Dynamic,Dynamic> a(2,2); a << 0,2,1,3; cout << "sum1(a) = " << sum1(a) << endl; // ok cout << "sum2(a) = " << sum2(a) << endl; // 现在正常工作! cout << "sum2(a) = " << sum2<double>(a) << endl; // 依然支持显式指定T // 测试Ref类型 Ref<const Matrix<double,Dynamic,Dynamic>> ref_a = a; cout << "sum2(ref_a) = " << sum2(ref_a) << endl; // 正常工作 // 测试Block切片 cout << "sum2(a.block(0,0,2,1)) = " << sum2(a.block(0,0,2,1)) << endl; // 也能支持 return 0; }
为什么这个方案有效?
DenseBase<Derived>是所有Eigen稠密容器的基类,包括Matrix、Ref、Block等,所以能匹配所有你需要的类型。- 编译器会自动推导
Derived参数,然后通过Derived::Scalar获取标量类型,完全不需要手动指定T。
适配你的基于T的类
如果你想在自己的template <typename T>类中使用这个逻辑,可以通过static_assert确保传入的Eigen容器的标量类型和类的T一致:
template <typename T> class MyMathClass { public: template <typename Derived> T computeSum(const DenseBase<Derived>& m) const { // 确保传入的容器标量类型和类的T一致 static_assert(std::is_same_v<typename Derived::Scalar, T>, "Scalar type of Eigen container must match class template type"); T result = T(0); // ... 你的计算逻辑 return result; } };
方案2:保留T模板,用SFINAE约束参数类型
如果你坚持让函数直接基于T模板,可以用SFINAE(Substitution Failure Is Not An Error)来约束参数必须是标量为T的Eigen稠密容器:
template <typename T, typename Derived> std::enable_if_t<std::is_same_v<T, typename Derived::Scalar>, T> sum2(const DenseBase<Derived>& m) { T x = T(0); for (int j=0; j<m.cols(); ++j) for (int i=0; i<m.rows(); ++i) x += m(i,j); return x; }
这个方案同样能让sum2(a)自动推导T=double,同时严格限制传入的容器标量类型必须是T,和你的类模板类型保持一致。
为什么你原来的写法失败?
当你传入Matrix<double,...>给sum2(const Ref<const Matrix<T,Dynamic,Dynamic>>&)时,编译器需要推导T,但Ref<Matrix<T,...>>和Matrix<double,...>是不同的类型——Ref是一个包装类,而Matrix是原始容器,模板推导无法自动将Matrix转换为Ref来匹配参数,因此必须显式指定T才能编译通过。
内容的提问来源于stack exchange,提问作者Brian Hawkins

