模板重载解决奇数 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)...);
}
我们想使用 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
:
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)...);
}