2

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

jlack
  • 305
  • 2
  • 13
  • it is because Eigen::Transpose is returning an expression template not a matrix. you could make your constructor be a template that accepts types that are convertibkle to an Eigen::Matrix3d to fix the problem, i think. – jwezorek Mar 08 '23 at 20:31
  • @jlack Did you try out the suggestion in my answer? Do you need any further clarification? – Ted Lyngmo Mar 09 '23 at 18:07
  • @jlack Great to hear! You're welcome! – Ted Lyngmo Mar 15 '23 at 15:14

1 Answers1

1

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
    }
};
Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108