I am trying to implement a function template ovl
such that ovl<Foo, Bar>(f)
will return the overload of f
taking (Foo, Bar)
, and very surprised with what happens with my naïve solution:
template <class... Args, class Ret>
constexpr auto ovl(Ret (*const f)(std::type_identity_t<Args>...)) { return f; }
void foo();
void foo(int);
void foo(int, int);
int main() {
ovl<int>(foo)(0);
}
prog.cc:26:5: fatal error: no matching function for call to 'ovl'
ovl<int>(foo)(0);
^~~~~~~~
prog.cc:6:16: note: candidate template ignored: couldn't infer template argument 'Ret'
constexpr auto ovl(Ret (*const f)(std::type_identity_t<Args>...)) { return f; }
^
The same error appears with GCC and Clang. What's more, it actually works when enumerating possible arities myself:
template <class Ret>
constexpr auto ovl(Ret (*const f)()) { return f; }
template <class Arg0, class Ret>
constexpr auto ovl(Ret (*const f)(std::type_identity_t<Arg0>)) { return f; }
template <class Arg0, class Arg1, class Ret>
constexpr auto ovl(Ret (*const f)(std::type_identity_t<Arg0>, std::type_identity_t<Arg1>)) { return f; }
// ... ad nauseam.
Interestingly, keeping Args...
but hardcoding the return type works as well:
template <class... Args>
constexpr auto ovl(void (*const f)(std::type_identity_t<Args>...)) { return f; }
It seems like partial explicit arguments are ignored when they are provided to a parameter pack, but why? And how can I ensure that they are considered when trying to disambiguate the function pointer?
Note: I have found the following workaround which bakes Args...
first before deducing Ret
, but am still interested in an answer as this is quite clunky.
template <class... Args>
struct ovl_t {
template <class Ret>
constexpr auto operator()(Ret (*const f)(Args...)) const { return f; }
};
template <class... Args>
constexpr ovl_t<Args...> ovl;