I am trying to use a function with a default argument as a function pointer template parameter:
template <void (*F)()>
class A {};
void foo1(int a = 0) {}
void foo2() {}
int main()
{
//A<foo1> a1; <-- doesn't work
A<foo2> a2;
}
The compiler error is:
main.cpp:7:7: error: could not convert template argument ‘foo1’ to ‘void (*)()’
Is there specific syntax for this to work? Or a specific language limitation? Otherwise, the alternative is to have two separate functions instead of a default parameter:
void foo1(int a) {}
void foo1() { foo1(0); }
Update
I understand that the signatures are different, but I'm wondering if there is a way to make this work conveniently without needing to modify all the functions with default parameters?
According to section 8.3.6 of the C++ standard,
If an expression is specified in a parameter declaration this expression is used as a default argument. Default arguments will be used in calls where trailing arguments are missing.
Since A<foo1>
is not a call of the function, default arguments are ignored. In fact, they are ignored in all contexts except the calls of the function, for example
typedef void (*FFF)();
FFF x = foo1;
will not compile, and produce the same message that you get when trying to use foo1
as a template parameter:
error: invalid conversion from ‘void (*)(int)’ to ‘void (*)()’
This makes sense, because evaluating default arguments is a separate step in the invocation:
8.3.6.9: Default arguments will be evaluated each time the function is called.
The presence of default arguments does not alter the signature of your function. For example, you cannot use a single-argument function with a default argument to override a no-argument virtual member function.
The signature of foo1
is void(int)
, not void()
. This is why it isn't convertible to void(*)()
.
You are confusing default arguments with overloading.
Default argument values are not part of the function type. You can't use foo1
as a function taking no arguments, because it does take one argument. The argument gets filled in for you if you don't mention it, but it's still there.
Your workaround involving a dispatching function sounds like a good solution. It could even be templated if you need it a lot.
I'm pretty sure that a function pointer has signature of the function with all default parameters expanded and function pointers cannot convert to a function pointer with a different signature. Finding this in the standard is a different matter, though...
I think the relevant clause from the standard is 8.3.5 [dcl.fct] paragraph 6:
... The return type, the parameter-type-list, the ref-qualifier, and the cv-qualifier-seq, but not the default arguments (8.3.6) or the exception specification (15.4), are part of the function type. [ Note: Function types are checked during the assignments and initializations of pointers to functions, references to functions, and pointers to member functions. —end note ]
Note that default arguments are the guys of the form = value
according to 8.3.6 [dcl.fct.default] paragraph 1:
If an initializer-clause is specified in a parameter-declaration this initializer-clause is used as a default argument. ...
It won't compile because foo1
has signature:
void foo1(int a);
which you're trying to stick into a pointer to:
void F()
The function signatures don't match. The fact that foo1
has a default parameter doesn't change the function's signature (it still can take in an int
).
A More Generic Solution
I'd say forget about the templates, they're only limiting you here.
Personally, the way I solve the callback problem is using function objects with argument binding. It can be done using the boost::function library, and binding default arguments with boost::bind (or std::bind1st
and std::bind2nd
).
These boost libraries are also built-in to the new C++11 standard, as std::function
, and std::bind
.
It's well worth taking a look at this, as it let's you do some very nice things, like providing default arguments to functions, or use class member functions as callbacks.
The sites I've linked to all have lots of example code, and the boost links have tutorials.