Passing a function with a default parameter value as a template parameter

I am a little puzzled by the following behavior. I pass a function with two parameters, one of which has a default value, as a template parameter and calls a function with one argument. Why doesn't it compile? And what is a solution / workaround?

#include <iostream> using namespace std; template<typename Function> void eval(Function function) { function(10); } void sum(int i, int j = 0) { cout << "Sum is " << i + j; } int main() { sum(10); // OK, of course :) eval(sum); // Error! } 

Please note that this question does not apply to calling a template function with a default parameter.

Error message:

 prog.cpp: In instantiation of 'void eval(Function) [with Function = void (*)(int, int)]': prog.cpp:15:10: required from here prog.cpp:6:10: error: too few arguments to function function(10); ^ 
+6
source share
3 answers

This is because an optional parameter is part of the function declaration. When you call with a function pointer, essentially the entire compiler knows the type of function pointer. So, this function(10) roughly translates to:

 void (*)(int, int) sm = function; // the equivalent happens at the type deduction step sm(10); // whoops, where is the second int 

The compiler will need a second argument because it does not know if sm points to sum , which has a default argument, or some other void foo(int a, int b) , which does not have a default argument.

+5
source

When you call sum :

 sum(10); 

compiler translates it to:

 sum(10, 0); 

There is no error because the arguments are matched. For the compiler, there is no sum accepting one argument. You can say that the preliminary compiler does the magic of passing 0 second argument sum . With templates, this magic precompiler does not exist, and the actual compiler matches sum with two, two, two arguments that it does not find, and therefore an error.

Like

 void foo(int); void foo(float); 

not compiled with foo(10.2); , where the double argument can be changed to either float or int . The compiler will not accept a float for you. In the same way, the compiler will not try to find the best match sum only for successful compilation.

+2
source

This is why function pointers are weak. You have two good answers why this does not work - the function template does not know that your function has a default argument.

The solution is to not pass a function pointer - pass a function object. In this case, you can rewrite lambda:

  eval([](int i){ return sum(i); }); 

which seems like a really annoying true way to basically just write sum in a way that really works. But such things happen periodically (what if sum was an overloaded name?), So it’s convenient to have only the lambdafying macro (requires C ++ 14):

 #define AS_LAMBDA(func) [&](auto&&... args) -> decltype(auto) { \ return func(std::forward<decltype(args)>(args)...); } 

so you can write:

 eval(AS_LAMBDA(sum)); 

This will work with default arguments, overloaded names, and even work to bind member functions:

 struct X { int i; int add(int j) { return i + j; } }; X x{42}; eval(AS_LAMBDA(x.add)); 
+2
source

All Articles