Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Overload resolution with variadic higher order functions

Lets say I have a variadic higher order function

template<typename F, typename ...Args>
void execution(F func, Args&&... args)
{
    func(std::forward<Args>(args)...);
}

Then for this overload set

void f() {}
void f(int arg) {}

Overload resolution will be impossible

int main()
{
    execution(f, 1);
    execution(f);
    return 0;
}

Yet if only either one of the two is provided, the program compiles

  1. Why does this happen ? Why template argument deduction fails?
  2. If I remove f() from the set and replace it with f(arg, arg2) there's still a problem. Is there a workaround, or do I always have to provide the type of the function as a template argument ?

    execution<void()>(f);
    
like image 761
Nikos Athanasiou Avatar asked Dec 14 '22 23:12

Nikos Athanasiou


2 Answers

When doing template type deduction, the compiler doesn't analyse how the types are used inside the function. Therefore when deducing the template parameters, the compiler sees no relation between the different template arguments. Thus the additional variadic template arguments don't matter at all, and the problem can be reduced to

template<typename Func> void execution(Func func);

void f();
void f(int);

execution(f);

With this minimized code, it should be obvious why template argument deduction fails.

You can resolve this by making your first argument explicitly dependent on the remaining template arguments, e.g.:

template<typename... Args>
 void execution(void (*func)(Args ...), Args ... args)
{
  func(std::forward<Args>(args) ...);
}
like image 74
celtschk Avatar answered Jan 11 '23 06:01

celtschk


You need an overload set object. This is an object that represents the entire overload set of functions of f:

struct f_overload_set {
  template<typename...As>
  auto operator()(As&&...as)->
    decltype(f(std::declval<As>()...))
  { return f(std::forward<As>(as)...); }
};

now pass f_overload_set{} to your template function.

templates function value arguments must be values, and in current C++ there is no first class value that represents an entire overload set of a function. The symbol f gets disambiguated into one overload at each point of use: but that requires an immediate context at the point of use. The above defers the disambiguation until we have the arguments handy.

like image 20
Yakk - Adam Nevraumont Avatar answered Jan 11 '23 06:01

Yakk - Adam Nevraumont