Emily L.
Emily L.

Reputation: 5931

Template overload resolution oddity VS2013

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

Answers (1)

Oktalist
Oktalist

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

Related Questions