Search code examples
c++eigen3

Ambiguous overload after arithmetic between different types of Eigen matrices


I have encountered a problem in a function overload for different kinds of Eigen matrices (a column and a matrix to be precise). The overload fails when the input is the result of some simple arithmetic. In particular:

#include <Eigen/Eigen>
#include <iostream>

typedef Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> MatD;
typedef Eigen::Matrix<double, Eigen::Dynamic,              1, Eigen::ColMajor> ColD;

ColD multiply(const ColD &A){ return 2. * A; }
MatD multiply(const MatD &A){ return 2. * A; }

int main()
{
  ColD A = ColD::Ones(10);
  MatD B = MatD::Ones(10,10);

  // overload is correct
  std::cout << multiply(A) << std::endl;
  std::cout << multiply(B) << std::endl;

  // compiler error: "error: call to 'multiply' is ambiguous"
  std::cout << multiply(A+A) << std::endl;
  std::cout << multiply(B+B) << std::endl;

  return 0;
}

I.e. the overload based on A and B is compiled correctly. But the compiler is unable to identify the correct overload when I do A+A and B+B. I could solve this by doing two lines, potentially using some temporary variable, but this I don't want to do. How can I get this overload to work for this 'one-liner'?

I presume it is a particularity of Eigen, but am so far unable to understand exactly which particularity and how I can avoid it.


Solution

  • As explained in the comment, Eigen's operators do not produce plain matrices or vectors but operation objects (e.g. Eigen::CwiseBinaryOp<...>).

    You can either convert the operation to a plain matrix like so...

    std::cout << multiply(ColD(A + A)) << std::endl;
    std::cout << multiply(MatD(B + B)) << std::endl;
    

    ... or make multiply a template that accepts all kinds of formats. The syntax becomes a bit ugly, however:

    template <typename Derived>
    Eigen::Matrix<
        typename Eigen::internal::traits<Derived>::Scalar,
        Eigen::internal::traits<Derived>::RowsAtCompileTime, 
        Eigen::internal::traits<Derived>::ColsAtCompileTime>
        multiply(const Derived& A) 
    {
        return 2.0 * A;
    }
    

    The template parameter is Derived, which can be a matrix, a vector, or any of Eigen's operation objects. The return value is a Eigen::Matrix whose characteristics we extract from Derived via the traits template.