18

For this simplified piece of code I'm getting following error:

error: too few arguments to function std::cout << f();

int g(int a = 2, int b = 1)
{
    return a + b;
}

template<class Func>
void generic(Func f)
{
    std::cout << f();
}

int main()
{
    generic(g);
}

I cannot clue the reason for why the default parameters of function f are not passing into a function generic. It behaves like f doesn't have any default parameters ...

What's wrong there?

How do I forward default parameters correctly?

2
  • 2
    I'm guessing that the time the function pointer is passed, it already selected an overload for the function that needs 2 parameters. Commented Sep 13, 2017 at 19:41
  • Language lawyer alert… a and b are function parameters. These parameters have default arguments of 2 and 1, rsp. So a is a parameter, and the value of a at run time is the argument. Commented Sep 14, 2017 at 9:56

2 Answers 2

22

g may have default arguments, but the type of &g is still int(*)(int, int), which is not a type that can be called with no arguments. Within generic, we can't differentiate that - we've already lost the context about default arguments.

You can just wrap g in a lambda to preserve the context:

generic([]{ return g(); });
Sign up to request clarification or add additional context in comments.

6 Comments

is there any possibility (without bind) to get a int(*)(int) or int(*)() from g ?
Maybe soon we will able to do this generic([]() => g());, right? :P (thanks)
@tobi303 You can't do it with bind at all - same problem as here. Has to be a lambda.
hmm that's interesting, it seems to be working that way. But how does lambda preserve the context ?
@ampawd Because g() is a valid expression, you're invoking it directly by name. It's the name lookup via g that gets you the default arguments, not through the type of g
|
7

I think the error message to this code quite nicely demonstrates why this isn't possible:

int g(int a=0,int b=0){return 0;}

typedef int (*F1)(int);

int main() {
    F1 x = g;
}

error: cannot initialize a variable of type 'F1' (aka 'int (*)(int)') with
an lvalue of type 'int (int, int)': different number of parameters (1 vs 2)
    F1 x = g;
       ^   ~

Even with default parameters, the type of g is still

int (*) (int,int)

and that's what gets deduced when you instantiate the template.

If for some reason you cannot use C++11 or later (i.e. no lambdas, see Barry's answer) and you don't mind a little boilerplate, then you can use a function object:

#include <iostream>

struct g_functor {
    int operator()(int a=0,int b=0){ return a;}
};

template <typename T> void foo(T t) { t(); }

int main() { foo(g_functor()); }

Note that you have to create an instance of g_functor to pass it as a parameter.

3 Comments

If you're going to go boilerplate, you should make g a function object instead - so you can still do generic(g)
yeah, nice solution with function object as well
@ampawd function objects are just the poor mans lambdas (or lambdas are just function objects with some fancier syntax). I was just curious to see how one would do it in C++98. For me this always helps to better understand the C++11 features as most of them were invented to overcome C++98 shortcomings

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.