Reputation: 2930
I'm working on code with a lot of classes that template on a scalar type T
and implement methods on containers like std::valarray<T>
. I'd like to add support for Eigen containers, but I'm having trouble figuring out a way to template only on the scalar type while maintaining some of Eigen's flexibility.
For example, the Eigen docs suggest using Eigen::DenseBase<Derived>
, but that seems to mean I'd have to change all my class defintions to template on the array type Derived
and then use Derived::Scalar
everywhere. Pretty intrusive just to support another container.
I could also just use Matrix<T,Dynamic,Dynamic>
, but this is pretty restrictive. For the cases I care about, I was thinking a reasonable middle ground would be to use Ref<Matrix<T,Dynamic,Dynamic>>
since that would cover Matrix
and slices thereof with one interface. But this doesn't seem to work, and I'm not sure why. Here's a concrete example of what I mean:
#include<iostream>
#include<Eigen/Dense>
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 T>
T
sum2(const Ref<const Matrix<T,Dynamic,Dynamic>> &m)
{
T 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;
}
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; // error
cout << "sum2(a) = " << sum2<double>(a) << endl; // ok
return 0;
}
But I get errors when I compile:
$ clang++ -std=c++17 -I/usr/include/eigen3 eig2.cpp -o eig2
eig2.cpp:33:29: error: no matching function for call to 'sum2'
cout << "sum2(a) = " << sum2(a) << endl; // error
^~~~
eig2.cpp:19:1: note: candidate template ignored: could not match 'Ref' against
'Matrix'
sum2(const Ref<const Matrix<T,Dynamic,Dynamic>> &m)
^
1 error generated.
Is there a way to implement sum2
so that it supports Matrix
and Ref
and doesn't require explicit template arguments to use it?
Upvotes: 4
Views: 1415
Reputation: 18807
The reason that
template <typename T>
T
sum2(const Ref<const Matrix<T,Dynamic,Dynamic>> &m)
does not work for Matrix<T,Dynamic,Dynamic>
is that C++ can't deduce template arguments and do type-conversion at the same time (Ref
is a different type than Matrix
, but it can be constructed with O(1) effort from it).
One alternative (requiring C++11 or higher) which allows you to keep the function body is:
template<typename Derived, typename T=typename Derived::Scalar>
T sum3(const MatrixBase<Derived> &m)
If you want to allow Array
as well, you can use DenseBase
, if you want to allow sparse matrices you can use EigenBase
.
However, this is not equivalent to the Ref
implementation with explicit type substitution, since if Derived
is an expression, it may get evaluated multiple times inside your function.
An alternative is to write
template <typename T>
T
sum_impl(const Ref<const Matrix<T,Dynamic,Dynamic>> &m)
{ /* here comes the actual implementation */ }
template<typename Derived>
typename Derived::Scalar sum4(const Eigen::MatrixBase<Derived>& m)
{
return sum_impl<typename Derived::Scalar>(m);
}
Upvotes: 7