问题
Is there a way to allow a template parameter of function pointer type to accept a function of any (rather than a specific) return type, when the function's return value is not actually used? Here's an MCVE to illustrate what I mean:
int returnInt(int) { return 0; }
void returnVoid(int) { }
template <int (*Func)(int)>
struct foo { void bar(int x) { Func(x); } };
int main(int, char *[]) {
foo<returnInt> a; // ok
foo<returnVoid> b; // argument of type "void (*)(int)" is incompatible
// with template parameter of type "int (*)(int)"C/C++(458)
}
I know I can do this, as a workaround:
template <typename ReturnType, ReturnType (*Func)(int)>
struct foo { void bar(int x) { Func(x); } };
int main(int, char *[]) {
foo<int, returnInt> a; // ok
foo<void, returnVoid> b; // ok
}
Or even this (which is worse in my opinion, because we lose the type check on the function type's argument types and could fall into SFINAE):
template <typename FuncType, FuncType *Func>
struct foo { void bar(int x) { Func(x); } };
int main(int, char *[]) {
foo<decltype(returnInt), returnInt> a; // ok
foo<decltype(returnVoid), returnVoid> b; // ok
}
But I wonder if there's a way to do it without adding an extra template parameter which has no other purpose.
回答1:
You can declare the template parameter as auto (since C++17) to make it working with any function pointer types.
template <auto Func>
struct foo { void bar(int x) { Func(x); } };
LIVE
And if you want to check the type with SFINAE:
template <auto Func, std::enable_if_t<std::is_function_v<std::remove_pointer_t<decltype(Func)>>> * = nullptr>
struct foo { void bar(int x) { Func(x); } };
LIVE
Or still declare the template parameter as function pointer which might return any types.
template <auto (*Func)(int)>
struct foo { void bar(int x) { Func(x); } };
LIVE
回答2:
I know I can do this, as a workaround:
[...]
But I wonder if there's a way to do it without adding an extra template parameter which has no other purpose.
Although the workaround by OP mentions additional template parameters, they are used as parameters to the primary template, encumbering the client to explicitly specify them in the template argument list when making use of the dispatch API.
The key here, w.r.t. to using additional template parameters, is (as shown in my detailed answer to the related follow-up question) specialization:
template <auto>
struct foo {
void bar(int x) = delete;
};
template <typename T, typename Return, Return (*Func)(T)>
struct foo<Func> {
void bar(int x) {
Func(x);
}
};
int returnInt(int) { return 0; }
void returnVoid(int) { }
int main() {
foo<returnInt> a; // OK (GCC and Clang)
foo<returnVoid> b; // OK (GCC and Clang)
}
The additional template parameters in the partial specialization are not visible to the client, and are entirely deducible from the non-type template parameter associated with the client API.
来源:https://stackoverflow.com/questions/65915344/allow-template-parameter-of-function-pointer-type-to-accept-functions-of-any-ret