I recently discovered that the following code compiles fine
void func(std::function<void()> cb);
func([]() -> asio::awaitable<void> {
...
co_return;
});
This leads me to guess that c++ doesn't properly distinguish between std::function<void()>
and std::function<asio::awaitable<void>()>
type arguments.
In fact the following code does fail to compile:
#include <boost/asio.hpp>
#include <functional>
namespace asio = boost::asio;
void func(std::function<void()> cb)
{}
void func(std::function<asio::awaitable<void>()> cb)
{}
int main()
{
func([]() -> asio::awaitable<void> {
co_return;
});
}
The compiler will complain that call of overloaded 'func(main()::<lambda()>)' is ambiguous.
The lambda can be converted to both
std::function<void()>
and
std::function<asio::awaitable<void>()>
because std::function
accepts any callable whose return type can be converted to the one specified in its template argument. Any type can be converted to void
(i.e. by ignoring the return value) and therefore it doesn't matter what the callable returns for the first specialization.
Because conversion of the lambda to either std::function
specialization is a user-defined conversion via std::function
's constructor and because both obviously use different constructors (as they are different target types), neither is considered better than the other in overload resolution making the overload resolution for the call ambiguous if you declare functions with both as parameter.
Similarly std::function
doesn't require function parameters to match exactly either. It is enough that the callable that is to be stored in the std::function
can be called with the types specified in its template argument as arguments to the callable, accepting any template argument deduction and implicit conversion as in a usual call expression.