The follow class template:
template <typename R, typename... Args>
class WrongFunction {
public:
void F1(R(*Fn)(Args...)) {}//error: C2091 function returns function
};
defines a template parameter API which expects:
- a return type
R, and
- a variadic number of arguments (of possible different types)
Args.
Now, a template parameter pack may be empty, which hides your actual error here. In the instantiation of of the WrongTemplate class template:
WrongFunction<FuncDef> errfunc;
you provide only a single template argument, which matches the first template parameter of the class template:
FuncDef is used as argument for R in WrongFunction
whereas you are providing no argument for template parameter pack.
Thus, the compilers' error message is quite telling: you are instantiating the WrongFunction class template with a template argument provided for the template parameter R, which semantically signifies a return type, but with a function type argument. Meaning, in the associated specialization, R is a function type;
void F1(R(*Fn)(Args...)) {}
// resolves to, for WrongFunction<FuncDef> specialization
void F1( (void)(*)(int, int) (*Fn)() ) {}
// ^^^^^^^^^^^^^^^^^^^ <- R
thus defining a function parameter that is declared to have the type "pointer to function (named Fn) with zero arguments that return a pointer to function with two arguments that return void". This is illegal, thus the compiler error.
In your other class template, you leverage specialization, constructing the primary class template API to have a single type template parameter, semantically intended to be provided arguments that are of type "pointer to function", as the the only partial specialization (as the primary template is not defined) is specifically partially specialized for this kind of argument.
(2) (optional) Is there a way to make WrongFunction work but not in the form as Function?
The template parameter API of WrongFunction expects the client to explicitly provide the return type for the intended function pointer (of the member function) and the argument types by separate template arguments. It basically does not rely on deduction. Your second approach with specialization is the common one, and instead of fixing the likely ill-designed WrongFunction, I will note a similar but slightly different design of Function, which embeds a function pointer into its type instead. Thus could be useful if the member function of a given specialization should only use delegation to a specific function, rather than allowing dynamic dispatch delegation via function pointers of specific function pointer type (as function parameter to the member function).
#include <utility>
template <auto> class Function;
template <typename R, typename... Args, R (*Fn)(Args...)>
class Function<Fn> {
public:
template <typename... FnArgs /* optionally SFINAE that FnArgs is Args */>
void F1(FnArgs &&... args) {
Fn(std::forward<FnArgs>(args)...);
}
};
void f(int a, int b) {}
int main() {
Function<f> func;
func.F1(1, 2);
}