Search code examples
c++c++11variadic-templateseigen

C++ Varaidic Tempate with Eigen, overloading functions with parameter list gives unexpected result


I have been testing C++ templating within Eigen and have come across a problem while developing when using tempates and templated parameter lists. As per the following example, the "Function 2" is printed for all calls - even though the 3rd and 4th call perfectly match the prototype. It seems that the templated parameter list takes precedence due to an ambiguity. Now the interesting part is that this only happens when matrices are wrapped in "Eigen::MatrixBase", if I send matrices directly it is not a problem. But this would not be optimal for the expression templates being used.

Could someone explain the reason for this when using MatrixBase and what could be a workaround? I understand that giving one function a different name will do the trick, but then the function will loose generality. Is there a better way to fix it?

Thank you for your time!

Minimal example showing the effect:

typedef Eigen::Matrix<float, 2, 2> QType;
typedef Eigen::Matrix<float, 1, 1> UType;

template <typename... ParamsType>
void fun(const Eigen::MatrixBase<QType> &Q,
         const Eigen::MatrixBase<UType> &U,
         const ParamsType & ...params)
{
  cout << "Function 1" << endl;
}

template <typename... ParamsType>
void fun(const Eigen::MatrixBase<QType> &Q,
         const ParamsType & ...params)
{
  cout << "Function 2" << endl;
}

template <typename... ParamsType>
void fun2(const QType &Q,
          const UType &U,
          const ParamsType & ...params)
{
  cout << "Function 1" << endl;
}

template <typename... ParamsType>
void fun2(const QType &Q,
          const ParamsType & ...params)
{
  cout << "Function 2" << endl;
}

int main(int argc, char *argv[])
{
  QType Q = QType::Random();
  UType U = UType::Random();

  int param = 0;

  fun(Q);
  fun(Q, param);
  fun(Q, U);
  fun(Q, U, param);

  fun2(Q);
  fun2(Q, param);
  fun2(Q, U);
  fun2(Q, U, param);

  return 0;
}

Output:

Function 2
Function 2
Function 2
Function 2

Function 2
Function 2
Function 1
Function 1

Solution

  • The reason is that UType is not the same as MatrixBase<UType>, but it inherits from it. That means passing U as a variadic parameter is closer to the function header (sorry for not being able to reference the corresponding section in the standard). See this simplified example which also illustrates the difference:

    #include <iostream>
    
    class A {};
    class B {};
    class C : public A { };
    class D : public B { };
    
    template<typename... P>
    void fun(const A&, const B&, const P & ...p)
    {
        std::cout << "funAB\n";
    }
    
    template<typename... P>
    void fun(const A&, const P & ...)
    {
        std::cout << "funAP\n";
    }
    
    int main()
    {
        A a;
        B b;
        C c;
        D d;
        fun(a,b); // funAB
        fun(c,d); // funAP, because d only inherits from B
    }
    

    If you explicitly want "Function1" to be called when UType is passed, just write that as the parameter (as in your fun2 implementation).