Why function pointer of overloaded function need static_cast?

Viewed 168

For the below code, I get the error

#include <iostream>
#include <functional>

using namespace std;

class A {};
class B {};

namespace N
{
    void func(A a, int z){}
    void func(B a, int z){}
}

void func_1(std::function<void(A, int)> x)
{
    A a;
    x(a, 1);
}

int main()
{
    func_1(N::func);
    
    return 0;
}

Error:

    main.cpp:23:19: error: cannot resolve overloaded function 'func' based on conversion to type 'std::function<void(A, int)>'
   23 |     func_1(N::func);

If we do the static cast for the func_1(N::func); as func_1(static_cast<void (&)(A, int)>(N::func));, then this work fine. But I would expect this to work without a cast.

2 Answers

std::function<void(A, int)> is more complicated than void(*)(A, int).

template< class F >
function( F f );

Initializes the target with std::move(f). If f is a null pointer to function or null pointer to member, *this will be empty after the call. This constructor does not participate in overload resolution unless f is Callable for argument types Args... and return type R.

You don't even know what constructors participate in overload resolution until you decide which N::func you mean.

One can conceive of an combined overload resolution and template argument deduction scheme that could "meet in the middle" by trying std::function<void(A, int)>::function<void(*)(A, int)> among (arbitrarily many) other valid instantiations of the constructor.

Problems abound with that.

  1. It has to provably arrive at an answer. In general there are infinite possible instantiations of the templates. You'd also want it to be able to pick int(*)(A, int) if you passed it int g(A, int).
  2. It really should agree with the current scheme where that arrives at an unambiguous answer.
  3. Each of the compiler vendors have to implement it correctly.

As a handy workaround you can provide this kind of func_1 overload.

...

void func_1(void(*x)(A, int))
{
    func_1(std::function<void(A, int)>{x});
}

Now it works as desired without static_cast: demo

Related