Why do I get "candidate expects 2 arguments, 1 provided" compilation error when forwarding functions with C++11?

I’m trying to understand function forwarding with lambdas on C++ by running this code:

#include <functional>

template<typename Func, typename... Args>
int do_something_1(int foo_1, Func&& func, Args&&... args, int foo_0 = 0) { 
    int var_1 = foo_0 + 2 * foo_1; 
    return std::forward<Func>(func)(var_1, std::forward<Args>(args)...);
};

int main (void) {
    return do_something_1(1, [](int var_1, int local_var) { return (var_1 + local_var); }, 4);
}

However it fails with this compilation error:

<source>: In instantiation of 'int do_something_1(int, Func&&, Args&& ..., int) [with Func = main()::<lambda(int, int)>; Args = {}]':
<source>:11:93:   required from here
<source>:7:36: error: no match for call to '(main()::<lambda(int, int)>) (int&)'
    7 |     return std::forward<Func>(func)(var_1, std::forward<Args>(args)...);
      |            ~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:7:36: note: candidate: 'int (*)(int, int)' (conversion)
<source>:7:36: note:   candidate expects 3 arguments, 2 provided
<source>:11:30: note: candidate: 'main()::<lambda(int, int)>'
   11 |     return do_something_1(1, [](int var_1, int local_var) { return (var_1 + local_var); }, 4);
      |                              ^
<source>:11:30: note:   candidate expects 2 arguments, 1 provided

And I don’t know how to correct it.

>Solution :

Non-terminal function parameter packs are a "non-deduced context" ([temp.deduct.type]/5):

The non-deduced contexts are:

  • […]
  • A function parameter pack that does not occur at the end of the parameter-declaration-list.

This means that the 4 which you pass as an argument will not be used to deduce Args, but is instead bound to foo_0.

Technically you can make this compile by providing template arguments explicitly:

int main (void) {
    return do_something_1<int(*)(int,int), int>(1, [](int var_1, int local_var) { return (var_1 + local_var); }, 4);
}

This works because the non-capturing lambda can convert to a function pointer, and explicitly supplying int as a second template argument causes the 4 to bind to args... instead of foo_0.

But in general I’d recommend avoiding non-terminal function parameter packs when possible.

Leave a Reply