Ambiguous call with Eigen types

125 Views Asked by At

Attempting to make a class that takes both a Eigen::Matrix3d and Eigen::Vector4d as constructor args and have run into an issue with ambiguity. Given the following test class,

class MyClass
{
  public:
   MyClass(const Eigen::Matrix3d& m)
    {
    }

    MyClass(const Eigen::Vector4d& v)
    {
    }
};

If I then do the following,

int main(int argc, char** argv)
{
    Matrix3d m;
    MyClass t1(m.transpose());
}

this fails to compile with the following error,

call of overloaded ‘MyClass(Eigen::Transpose<Eigen::Matrix<double, 3, 3> >)’ is ambiguous
  516 |     MyClass t1(m.transpose());
      |                             ^
note: candidate: ‘MyClass::MyClass(const Vector4d&)’
  561 |     MyClass(const Eigen::Vector4d& v)
      |     ^~~~~~~
note: candidate: ‘MyClass::MyClass(const Matrix3d&)’
  556 |     MyClass(const Eigen::Matrix3d& m)

It's not clear to me how to resolve this issue

1

There are 1 best solutions below

0
Ted Lyngmo On BEST ANSWER

You can create constructor templates to both accept Eigen::Transpose<...> objects (that are returned by transpose()) and concrete Matrix3d and Matrix4d objects.

I assume the operations in your two separate constructors will be very similar so you can combine them:

class MyClass {
public:
    // One constructor template accepting both Matrix3d and Matrix4d:
    template<class Scalar, int Rows, int Cols, int Options, int MaxRows, int MaxCols>
    MyClass(const Eigen::Matrix<Scalar, Rows, Cols, Options, MaxRows, MaxCols>& m)
    {
        // here you can do what you did in the two separate constructors before
        std::cout << Rows << ',' << Cols << '\n';
    }

... and add a delegating constructor template for the Transpose objects:

    // delegating constructor taking an Eigen::Transpose object:
    template<class Scalar, int Rows, int Cols, int Options, int MaxRows, int MaxCols>
    MyClass(const Eigen::Transpose<
            Eigen::Matrix<Scalar, Rows, Cols, Options, MaxRows, MaxCols>>& t) :
        // delegate to the constructor shown above:
        MyClass(Eigen::Matrix<Scalar, Rows, Cols, Options, MaxRows, MaxCols>(t))
    {
        std::cout << "(was delegated)\n";
    }
};

Now all the following cases will work:

int main() {
    Eigen::Matrix3d m3;
    Eigen::Matrix4d m4;

    MyClass t3(m3);              // no delegation
    MyClass t4(m4);              // no delegation
    MyClass t3t(m3.transpose()); // delegating
    MyClass t4t(m4.transpose()); // delegating
}

Output:

3,3
4,4
3,3
(was delegated)
4,4
(was delegated)

If you only want to accept transpositions, remove the first constructor and the delegation:

class MyClass {
public:
    template<class Scalar, int Rows, int Cols, int Options, int MaxRows, int MaxCols>
    MyClass(const Eigen::Transpose<
            Eigen::Matrix<Scalar, Rows, Cols, Options, MaxRows, MaxCols>>& t)
    {
        Eigen::Matrix<Scalar, Rows, Cols, Options, MaxRows, MaxCols> m(t);
        // use `m` here
    }
};