可调用参数的模板专业化不按预期工作

时间:2018-04-13 17:03:47

标签: c++ c++11

下面的代码抱怨dp1“这一术语不会评估为带有0个参数的函数”。我希望dp1符合IsInstantiation的{​​{1}}专精,但它会出错。

我如何更改此代码以使其按预期工作?我使用的是VS2015,无法使用Dispatchstd::invoke

std::is_invokable

1 个答案:

答案 0 :(得分:3)

您的代码存在一些问题。

  1. 这是对forward

    的错误使用
    return f(std::forward(args)...);
    

    std::forward的函数参数有意为非推导的上下文。所以这个电话永远不会成功。您想要f(std::forward<Args>(args)...)

  2. 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友好的(另外,可以返回参考)。

  3. 您的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 {};