Function with default parameter as template type

2019-06-28 05:46发布

问题:

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?

回答1:

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.



回答2:

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.



回答3:

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.



回答4:

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. ...



回答5:

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.