用 C++ 20 个概念实现 std::is_invocable_r

Implementing std::is_invocable_r with C++ 20 concepts

我正在尝试使用 C++ 20 的概念实现 std::is_invocable<R, Callable, Args...>,尽可能少地使用 STL 的帮助,并且不使用 std::invoke

这是我目前的做法。虽然它会导致编译错误(msvc): error C3864: 'is_invocable_r': requires clause is incompatible with the declaration

template<class R, class Fn, class... ArgTypes>
    requires requires(Fn fn, ArgTypes... arg_types)
    {
        { std::forward<Fn>(fn)(std::forward<ArgTypes>(arg_types)...) } -> std::same_as<R>;
    }
struct is_invocable_r : std::true_type {};

template<class R, class Fn, class... ArgTypes>
struct is_invocable_r : std::false_type {};

这样做的正确方法是什么?

两个问题。

  1. 您没有专门化模板。您正在重新声明它。必须使用模板 ID 声明部分特化。 IE。 is_invocable_r<...>.
  2. 部分特化应该比主模板声明更多约束,而不是更少。而且它必须是主要参数方面的更特殊情况;我们不能只是重复完全相同的参数列表并将其称为特化。

考虑到这一点,定义特征的更简单方法可能是:

template<class R, class Fn, class... ArgTypes>
struct is_invocable_r :
    std::bool_constant<
        requires(Fn fn, ArgTypes... arg_types)
        {
            { std::forward<Fn>(fn)(std::forward<ArgTypes>(arg_types)...) } -> std::same_as<R>;
        }
    > 
{};

由于必须显式提供所有模板参数,并且没有空间专门化您的 class 模板(不能在 class 模板中的参数包后添加参数),我们根本无法专精。

相反,我们只是将 requires 表达式的结果输入 bool_constant,因为它是最终决定特征值的表达式的结果1.


1 - 您的 requires 表达式不包含指向成员的指针,因此它不等同于 std::is_invocable_r 给出的结果。需要更多的改进(甚至可能通过专业化)来涵盖这种情况。