Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dealing with <unresolved overloaded function type> when passing function pointer to function

Tags:

c++

c++11

Let's consider:

void goo () {
    std::cout << "void goo ()\n";
}   

int goo (int) {
    std::cout << "int goo (int)\n";
    return 42;
}

And now I want to call one of those functions using some wrapper function defined like this:

template <typename F, typename... A>
void c (F&& f, A&&... a) {
    f (std::forward<A> (a)...);
}

With usage:

c (&goo, 10); // (X)
c (&goo);     // (Y)

Both cases fail (GCC 5.3.1) with appropriate errors:

error: no matching function for call to ‘c(<unresolved overloaded function type>, int)’
error: no matching function for call to ‘c(<unresolved overloaded function type>)’

As far as I am concerned the fail is because compiler could not choose appropriate overload when it has to initialize f object (too few information).

As a solution of course I can write usage calls like this:

c (static_cast<int (*) (int)> (&goo), 10);
c (static_cast<void (*) ()> (&goo));

To tell the compiler which pointer I really want to use.

Writing this static_cast for me makes code much more uglier, so I wrote a wrapper function for converting function pointer to appropriate one using template:

template <typename R, typename... Args>
using Funptr_t = R (*) (Args...);

template <typename R, typename... Args>
Funptr_t<R, Args...> funptr (Funptr_t<R, Args...> ptr) {
    return static_cast<Funptr_t<R, Args...>> (ptr);
}

and now usage looks much better:

c (funptr<int, int> (&goo), 10);
c (funptr<void> (&goo));

My question is: Do you have any better idea how to deal with that kind of situation? I am pretty sure this happen very often in generic code. So, please advice me something.

The ideal solution would be if I could use (X) and (Y) directly, so the magic trick with pointing appropriate overload would be done using A... and hidden to the caller.

like image 890
Artur Pyszczuk Avatar asked Mar 09 '23 04:03

Artur Pyszczuk


2 Answers

I mean, you can always specify the template parameter explicitly:

c<int(int)>(&goo, 10);
c<void()>(&goo);

AFAIK there is no way to do that in c, because F cannot be deduced (ambiguity between the overloads), and to make the compiler deduce the correct type, you need to change something on the caller side.

like image 184
Rakete1111 Avatar answered Apr 29 '23 13:04

Rakete1111


If C++14 is available, then generic lambdas offer a solution to the "passing overload sets as function arguments" problem:

#define FWD(arg) static_cast<decltype(arg)&&>(arg)
auto goo_fn = [](auto&&... args) -> decltype(goo(FWD(args)...)) {
    return goo(FWD(args)...);
};

c(goo_fn, 10); // ok
c(goo_fn);     // ok

goo_fn is a lambda that transparently (as much as possible anyway) represents the overload set of goo. The trailing decltype is necessary for two reasons: (1) to ensure that the return type is a reference if that overload of goo's is and (2) to ensure SFINAE-friendliness (which decltype(auto) would not.

like image 20
Barry Avatar answered Apr 29 '23 14:04

Barry