Overload resolution occurs only when (a) you are calling the name of a function/operator, or (b) are casting it to a pointer (to function or member function) with an explicit signature.
Neither is occurring here.
std::function
takes any object that is compatible with its signature. It doesn't take a function pointer specifically. (a lambda is not a std function, and a std function is not a lambda)
Now in my homebrew function variants, for signature R(Args...)
I also accept a R(*)(Args...)
argument (an exact match) for exactly this reason. But it means that it elevates "exact match" signatures above "compatible" signatures.
The core problem is that an overload set is not a C++ object. You can name an overload set, but you cannot pass it around "natively".
Now, you can create a pseudo-overload set of a function like this:
#define RETURNS(...) \
noexcept(noexcept(__VA_ARGS__)) \
-> decltype(__VA_ARGS__) \
{ return __VA_ARGS__; }
#define OVERLOADS_OF(...) \
[](auto&&...args) \
RETURNS( __VA_ARGS__(decltype(args)(args)...) )
this creates a single C++ object that can do overload resolution on a function name.
Expanding the macros, we get:
[](auto&&...args)
noexcept(noexcept( baz(decltype(args)(args)...) ) )
-> decltype( baz(decltype(args)(args)...) )
{ return baz(decltype(args)(args)...); }
which is annoying to write. A simpler, only slightly less useful, version is here:
[](auto&&...args)->decltype(auto)
{ return baz(decltype(args)(args)...); }
we have a lambda that takes any number of arguments, then perfect forwards them to baz
.
Then:
class Bar {
std::function<void()> bazFn;
public:
Bar(std::function<void()> fun = OVERLOADS_OF(baz)) : bazFn(fun){}
};
works. We defer overload resolution into the lambda that we store in fun
, instead of passing fun
an overload set directly (which it cannot resolve).
There has been at least one proposal to define an operation in the C++ language that converts a function name into an overload set object. Until such a standard proposal is in the standard, the OVERLOADS_OF
macro is useful.
You could go a step further, and support cast-to-compatible-function-pointer.
struct baz_overloads {
template<class...Ts>
auto operator()(Ts&&...ts)const
RETURNS( baz(std::forward<Ts>(ts)...) );
template<class R, class...Args>
using fptr = R(*)(Args...);
//TODO: SFINAE-friendly support
template<class R, class...Ts>
operator fptr<R,Ts...>() const {
return [](Ts...ts)->R { return baz(std::forward<Ts>(ts)...); };
}
};
but that is starting to get obtuse.
Live example.
#define OVERLOADS_T(...) \
struct { \
template<class...Ts> \
auto operator()(Ts&&...ts)const \
RETURNS( __VA_ARGS__(std::forward<Ts>(ts)...) ); \
\
template<class R, class...Args> \
using fptr = R(*)(Args...); \
\
template<class R, class...Ts> \
operator fptr<R,Ts...>() const { \
return [](Ts...ts)->R { return __VA_ARGS__(std::forward<Ts>(ts)...); }; \
} \
}