c++c++17eigen3ambiguous

Ambiguous call with Eigen types


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


Solution

  • 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
        }
    };