下面的代码抱怨dp1
“这一术语不会评估为带有0个参数的函数”。我希望dp1符合IsInstantiation
的{{1}}专精,但它会出错。
我如何更改此代码以使其按预期工作?我使用的是VS2015,无法使用Dispatch
或std::invoke
。
std::is_invokable
答案 0 :(得分:3)
您的代码存在一些问题。
这是对forward
:
return f(std::forward(args)...);
std::forward
的函数参数有意为非推导的上下文。所以这个电话永远不会成功。您想要f(std::forward<Args>(args)...)
Invoke
SFINAE不友好:
template <typename F, typename... Args>
constexpr auto Invoke(F &&f, Args &&... args)
{ ... }
如果F
确实可以使用Args...
调用,则此函数可以正常工作。如果不是,那么这是一个硬编译错误,因为问题只出现在 body 中,这超出了替换的“直接上下文”。结果,它不是“替代失败”(SFINAE中的SF),它是一个实例化失败。在HasNoArgs
中,您正在尝试查看是否可以调用它 - 这意味着您需要替换失败。为此,您需要将表达式提升到直接上下文中:
template <typename F, typename... Args>
constexpr auto Invoke(F &&f, Args &&... args)
-> decltype(f(std::forward<Args>(args)...))
{ return f(std::forward<Args>(args)...); }
现在,它是SFINAE友好的(另外,可以返回参考)。
您的HasNoArgs
检查的次数超出您的预期:
template <typename T>
struct HasNoArgs<T, std::void_t<decltype(Invoke(T{}))>> : std::true_type {};
这要求可以在没有参数的情况下调用T
,现在可以修复上面#1和#2之后的参数。但它也要求T{}
是一个有效的表达式。非默认可构造的callables将无法通过此测试,但不是因为测试声称它正在寻找。要解决此问题,请使用std::declval
:
template <typename T>
struct HasNoArgs<T, std::void_t<decltype(Invoke(std::declval<T>()))>> : std::true_type {};