8

I am a bit puzzled with the following behavior. I pass a function with two parameter, one having a default value, as a template parameter and call the function with one argument. Why does it fail to compile? And, what is the 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!
}

Note that this question is not about calling a templated function with default parameter.

The 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);
          ^
3
  • It would help if you also provide the error message. Commented May 19, 2016 at 5:29
  • 1
    @perreal No! That's about a templated function with default parameter. My question is about passing a function with default parameter as a template parameter to another function. Commented May 19, 2016 at 5:45
  • @EissaN. you mean a 'function', not a 'templated function'. sum is not a template Commented May 19, 2016 at 6:40

3 Answers 3

5

That's because the optional parameter is part of function declaration. When you call using a function pointer, essentially all the compiler knows is the type of the function pointer. So this line 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 the second argument because it has no way of knowing whether sm is pointing to sum which has a default argument, or some other void foo(int a, int b) which doesn't have a default argument.

Sign up to request clarification or add additional context in comments.

Comments

4

This is why function pointers are weak. You got two good answers of why this doesn't work - the function template doesn't know your function has a defaulted argument.

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

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

which seems like a really annoyingly verbose way of basically just writing sum in a way that actually works. But this sort of thing comes up periodically (what if sum were an overloaded name?) so it's handy to just have a lambdafying macro around (C++14 required):

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

so you can write:

eval(AS_LAMBDA(sum));

That will work with defaulted 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));

Comments

2

When you call sum:

sum(10);

compiler translates it to:

sum(10, 0);

There is no error, as arguments are matched. For the compiler there exist no sum taking one argument. You can say a pre-compiler doing the magic of passing 0 to second argument to sum. With templates this magical pre-compiler doesn't exist, and the actual compiler matches sum with two strictly two arguments, which it doesn't find, and hence the error.

Just like

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

will fail to compile with foo(10.2); where argument double can be changed to either float or int. The compiler will not assume float for you. Similarly, compiler will not try to find best match of sum just for the sake of successful compilation.

2 Comments

"magic" aside, it would be more correct to say that the default parameter isn't part of the type definition of the function. Which is all the template can see.
Is there a way to work around this? In c++14, I think you could wrap up sum inside a variadic lambda I think. eval( [](auto...args){ return sum(std::forward<decltype(args)>(args)...);});

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.