4

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.

Artur Pyszczuk
  • 1,920
  • 1
  • 16
  • 23

2 Answers2

4

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.

Rakete1111
  • 47,013
  • 16
  • 123
  • 162
3

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.

Barry
  • 286,269
  • 29
  • 621
  • 977
  • This is a fairly verbose solution, and I'm working on a [pair](https://wg21.link/p0573) of [proposals](https://wg21.link/p0644) to reduce that to just `[](args...) => goo(>>args...)`. Guess we'll see. – Barry Sep 04 '17 at 15:44
  • epic. btw, FWD == std::forward(args)... – Jay Yang Sep 18 '20 at 07:01
  • @JayYang The way I have it defined will compile faster, otherwise with the same behavior. – Barry Sep 18 '20 at 13:03