Reputation: 3196
Please consider the following example:
#include <iostream>
#include <future>
std::size_t calc_something(std::size_t lim_)
{
std::size_t result = lim_ * 10;
return result;
}
void calc_something(std::size_t lim_, std::promise<std::size_t> &promise_)
{
std::size_t result = lim_ * 10;
promise_.set_value(result);
}
void async_calc()
{
std::future<std::size_t> async_calc = std::async(calc_something, 5);
std::cout<< "async_calc = " << async_calc.get() <<std::endl;
}
I am still new to multi-threading, but why -on earth- can't std::async
pick the correct overload? The second overload uses a reference to an std::promise
object.
I've looked at this question here but it doesn't explain why. Also, I do not get an ambiguity error.
The error I get is:
error: no matching function for call to 'async' std::future<std::size_t> async_calc = std::async(calc_something, 5);
Upvotes: 2
Views: 815
Reputation: 41840
Overload sets cannot be sent as parameter as is, it must be converted to a function pointer first, or must be lifted into an object.
Overload sets must be converted since only the name of the function denote the complete set of functions, but you must send only one of those. How the compiler chooses the right one depends on the arguments you sent to it. Calling it provides the type of the parameters, but converting to a function pointer also provide the compiler with enough information about which overload must be sent.
Converting to a function pointer is usually the easy way:
auto function = static_cast<std::size_t(*)(std::size_t)>(calc_something);
std::future<std::size_t> async_calc = std::async(function, 5);
Lifting is done using a lambda:
std::future<std::size_t> async_calc = std::async([](auto lim) { return calc_something(lim); }, 5);
Lifting here is possible since you call the overload set, so there is a parameter the compiler can choose.
It just cannot delay that decision for overload sets, but for lambda and template function it can.
Upvotes: 3
Reputation: 10614
Overload resolution happens based on types of arguments specified at the call site. When you're calling std::async
, you're not calling calc_something
but passing a pointer to the function to std::async
. There are no calc_something
call arguments and no way to resolve which of the two overloads' address to pass to std::async
.
The compiler cannot use the subsequent arguments of std::async
to resolve the overload. From the compiler's perspective, all std::async
arguments are unrelated and nothing implies they will be used to invoke calc_something
. In fact, you can call std::async
with arguments of types different from those calc_something
accepts, and it will work because they will get converted when calc_something
is invoked.
In order to resolve this ambiguity, you must explicitly cast the pointer to calc_something
to the exact type of the function pointer, matching one of the overloads.
std::async((std::size_t (*)(std::size_t))calc_something, 5);
Upvotes: 4
Reputation: 24788
std::async()
is a function template and you pass calc_something
as an argument to it. However, there are two functions called calc_something
due to overloading. The calc_something()
overload has to be selected before the template deduction of std::async()
's arguments takes place.
std::future<std::size_t> async_calc = std::async(calc_something, 5);
In the code above, which overload will be passed to std::async()
?
std::size_t calc_something(std::size_t);
void calc_something(std::size_t, std::promise<std::size_t> &);
You have to specify:
std::async(static_cast<std::size_t(*)(std::size_t)>(calc_something), 5);
Upvotes: 2