Search code examples
c++templatesoverloadingfunction-templatesoverload-resolution

Why is the compiler not selecting my function-template overload in the following example?


Given the following function templates:

#include <vector>
#include <utility>

struct Base { };
struct Derived : Base { };

// #1
template <typename T1, typename T2>
void f(const T1& a, const T2& b)
{
};

// #2
template <typename T1, typename T2>
void f(const std::vector<std::pair<T1, T2> >& v, Base* p)
{
};

Why is it that the following code always invokes overload #1 instead of overload #2?

int main()
{
    std::vector<std::pair<int, int> > v;
    Derived derived;

    f(100, 200);  // clearly calls overload #1
    f(v, &derived);         // always calls overload #1

    return 0;
}

Given that the second parameter of f is a derived type of Base, I was hoping that the compiler would choose overload #2 as it is a better match than the generic type in overload #1.

Are there any techniques that I could use to rewrite these functions so that the user can write code as displayed in the main function (i.e., leveraging compiler-deduction of argument types)?


Solution

  • You can either do this:

    f(v, static_cast<Base*>(&derived));
    

    Or use SFINAE to remove the first function as a selection candidate:

    // Install boost library and add these headers:
    #include <boost/utility/enable_if.hpp>
    #include <boost/type_traits.hpp>
    
    // #1 - change it to look like this (note the keyword void changed positions)
    template <typename T1, typename T2>
    typename boost::disable_if<
       typename boost::is_convertible<T2, Base*>, void>::type
    f(const T1& a, const T2& b)
    {
    };
    
    // #2 - this one can stay the same
    template <typename T1, typename T2>
    void f(const std::vector<std::pair<T1, T2> >& v, Base* p)
    {
    };