Search code examples
c++variadic-functionsoverload-resolution

Overload resolution with parameter pack


Say I have these two methods:

// Overload 1
template <typename T, typename... Args>
void Foo(Args&&... args)
{
    // do things with T, args...
}

// Overload 2
template <typename T, typename... Args>
void Foo(std::function<void(T&)> && func, Args&&... args)
{
    // do things with func, T, args...
}

I'm trying to call it like so:

Foo<MyClass>([](auto& my_class){
    // do things with my_class
});

I'm intending to call Overload 2. The problem is it actually resolves to Overload 1. I can get it working by making an explicit std::function<void(MyClass&)> func variable and passing that after removing the r-value on the std::function, but I'd like to try and get it working with the lambda. It also works with the lambda if I provide another parameter, but I won't have another parameter to give it.

Shouldn't the method with the std::function argument be more specialized? Why is it choosing the wrong overload? I've read the overload resolution rules, but am not seeing/understanding the reason for the behavior.

Given the fact it works with the explicit parameter, I'm assuming it has something to do with the implicit conversion from the lambda into the std::function, that conversion is ranked lower than just using Args, even if it is more specialized. Is there anything I can do to keep the lambda?


Solution

  • Shouldn't the method with the std::function argument be more specialized?

    Yes, Overload 2 is more specialized than overload 1

    Why is it choosing the wrong overload?

    Lambda is NOT a std::function, so first overload is a better match (as exact match).

    Is there anything I can do to keep the lambda?

    Use regular template parameter instead of std::function:

    template <typename T, typename F, typename... Args>
    requires (std::invocable<F, T&>)
    void Foo(F&& func, Args&&... args)
    {
        // ...
    }
    

    Demo