Reputation: 5931
We want to use std::async
to launch jobs to an application-wide thread pool. To do this we implement two wrappers to the two std::async
signatures in our own namespace x
. So x::async(f, a, b)
will launch f(a,b)
into a thread pool queue. And x::async(std::launch::deferred, f, a, b)
will just forward to std::async
. It's a convenient one-stop-shop for launching jobs without having to stop and think about which functions to use.
When implementing the two overloads (with and without the launch policy) I encounter problems with the wrong template overload being resolved which results in a compile time error. I tried the code in GCC 5.2.0 and it compiles fine, leading me to suspect a Visual Studio bug (wouldn't be the first).
Below is a minimal example that shows the error I'm experiencing.
#include <future>
#include <utility>
#include <type_traits>
namespace x {
template< class Function, class... Args>
std::future<std::result_of_t<std::decay_t<Function>(std::decay_t<Args>...)>> async(Function&& f, Args&&... args){
return std::async(std::launch::async, std::forward<Function>(f), std::forward<Args>(args)...);
}
template< class Function, class... Args>
std::future<std::result_of_t<std::decay_t<Function>(std::decay_t<Args>...)>> async(std::launch policy, Function&& f, Args&&... args){
return std::async(policy, std::forward<Function>(f), std::forward<Args>(args)...);
}
}
int main(){
std::function<void(std::size_t, std::size_t)> f = [](std::size_t a, std::size_t b) { };
auto ftr = x::async(f, 2, 3);
}
Here, instead of dispatching to my thread-pool I just forward to std::async
for simplicity, it still shows the same error.
The compile error I'm getting is:
vc\include\xrefwrap(58): error C2064: term does not evaluate to a function taking 1
arguments
vc\include\xrefwrap(118) : see reference to class template instantiation
'std::_Result_of<_Fty,int>' being compiled
with
[
_Fty=int
]
project\source.cpp(18) : see reference to class template instantiation
'std::result_of<int (int)>' being compiled
Which indicates that it's actually resolving the call: x::async(f,2,3)
to x::async(policy, function, args...)
overload and converting the std::function
to std::launch
and taking 2
as a callable function called with argument 3
... By commenting out the overload with the launch policy, the code compiles fine, further strengthening my beliefs that it's a visual studio bug.
I'm asking for another pair off eyes to verify it's not my code at fault before I submit it to Microsoft.
Upvotes: 1
Views: 92
Reputation: 14714
This appears to be due to Visual Studio not implementing N3462 (SFINAE-friendly result_of
) which is part of C++14. It is instructive to examine how Visual Studio's implementation of std::async
works around this problem by using std::enable_if
:
template <class Function, class... Args>
std::future<std::result_of_t<
std::enable_if_t<
! std::is_same<std::decay_t<Function>, std::launch>::value,
std::decay_t<Function>
>(std::decay_t<Args>...)
>> async(Function&& f, Args&&... args) {
return std::async(std::launch::async, std::forward<Function>(f), std::forward<Args>(args)...);
}
template <class Policy, class Function, class... Args>
std::future<std::result_of_t<
std::enable_if_t<
std::is_same<Policy, std::launch>::value,
std::decay_t<Function>
>(std::decay_t<Args>...)
>> async(Policy policy, Function&& f, Args&&... args) {
return std::async(policy, std::forward<Function>(f), std::forward<Args>(args)...);
}
You might also avoid result_of
entirely by using decltype
:
template <class Function, class... Args>
auto async(Function&& f, Args&&... args)
-> std::future<decltype(std::forward<Function>(f)(std::forward<Args>(args)...))> {
return std::async(std::launch::async, std::forward<Function>(f), std::forward<Args>(args)...);
}
template <class Function, class... Args>
auto async(std::launch policy, Function&& f, Args&&... args)
-> std::future<decltype(std::forward<Function>(f)(std::forward<Args>(args)...))> {
return std::async(policy, std::forward<Function>(f), std::forward<Args>(args)...);
}
Upvotes: 1