模板重载解决奇数 VS2013

Template overload resolution oddity VS2013

我们想使用 std::async 将作业启动到应用程序范围的线程池。为此,我们在自己的命名空间 x 中为两个 std::async 签名实现了两个包装器。所以 x::async(f, a, b) 将启动 f(a,b) 到线程池队列中。而 x::async(std::launch::deferred, f, a, b) 只会转发到 std::async。这是启动工作的便捷一站式服务,无需停下来考虑使用哪些功能。

在实现两个重载(有和没有启动策略)时,我遇到了错误的模板重载被解决的问题,这导致了编译时错误。我尝试了 GCC 5.2.0 中的代码,它编译得很好,让我怀疑一个 Visual Studio 错误(不会是第一个)。

下面是一个显示我遇到的错误的最小示例。

#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);
}

在这里,为了简单起见,我没有分派到我的线程池,而是转发到 std::async,它仍然显示相同的错误。

我得到的编译错误是:

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

这表明它实际上正在解析调用:x::async(f,2,3)x::async(policy, function, args...) 重载并将 std::function 转换为 std::launch 并将 2 作为可调用使用参数 3 调用的函数...通过使用启动策略注释掉重载,代码编译得很好,进一步加强了我的信念,即它是一个 visual studio 错误。

在我将代码提交给 Microsoft 之前,我需要另一双眼睛来验证它不是我的错误。

这似乎是由于 Visual Studio 没有实现 N3462(SFINAE 友好 result_of),它是 C++14 的一部分。通过使用 std::enable_if:

检查 Visual Studio 对 std::async 的实现如何解决这个问题很有启发意义
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)...);
}

您也可以通过使用 decltype:

完全避免 result_of
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)...);
}