检查一个类型是一个包含泛型 lambda 的函子

Check a type is a functor including generic lambda

我可以写一个 trait 元函数来判断一个类型是不是仿函数吗? 有大量代码可以使用 decltype(&T::operator()) 的 SFINAE 检查函子,例如

template<class T>
struct is_functor {
    template<class F>
    static auto test(decltype(&F::operator())) -> std::true_type;
    template<class F>
    static auto test(...) -> std::false_type;
    static constexpr bool value = decltype(test<T>(0))::value;
};

但是,这不适用于通用 lambda,因为通用 lambda 的 operator() 是一个模板函数。

通用 lambda 版本的有限情况下有一些代码对通用 lambda 的参数类型进行了一些限制。例如,如果 lambda 表达式包含任何对 int 类型无效的表达式(例如成员访问操作),则此处的答案 () 将不起作用。

我不需要任何通用性。事实上,我只需要知道一个类型可以是一个只接受一个参数的仿函数。

如何实现我的 is_functor

用例:

我正在尝试验证给定参数是否为模板函数的仿函数,也就是说,我想要一些重载的模板函数,例如:

template<class F, class = enable_if_t<is_functor<std::decay_t<F>>::value>>
auto make_func(F &&f) { return std::forward<F>(f); }
template<class F, class = enable_if_t<!is_functor<std::decay_t<F>>::value>>
auto make_func(F &&f) { return [f=std::forward<F>(f)] (auto&&) { return f; }; }

没有正确的方法来做到这一点(至少在我们得到静态反射之前)。你能做的最好的事情就是检查一个对象是否 callable 具有一定的置信度:

  1. 尝试获取其 operator() 地址。如果失败,则对象可能是 non-callable 或者它的 operator() 可能是 overloaded/templated.

  2. 尝试使用为常用函数提供接口的虚拟 any_type 实例调用对象。这可能会帮助您推断出它的数量。

  3. 如果一切都失败了,强制用户以某种方式帮助推导元数或手动指定元数。

解决这个问题的一种方法是使用一组 deduced_arity 标签:

namespace deduced_arity
{
    template <std::size_t TS>
    struct deducible_t : std::integral_constant<std::size_t, TS>
    {
    };

    struct undeducible_t
    {
    };

    constexpr undeducible_t undeducible{};
    constexpr deducible_t<1> unary{};
}

您还需要某种 function_traits 实现,它将静态地告诉您 函数对象 的确切元数。这可以是 found in Boost.

那你还需要an implementation of any_type.

之后,您可以使用类似于以下类型特征的东西来检查 函数对象 是否可以 被重载,使用检测成语:

template <typename T>
using is_not_overloaded_impl = decltype(&T::operator());

template <typename T>
using is_not_overloaded =
    std::experimental::is_detected<is_not_overloaded_impl, T>;

然后你可以使用 if constexpr(...) 链(或任何其他 compile-time 分支机制) 来做出很好的猜测 - 例如:

if constexpr(is_not_overloaded<T>{})
{
    // use `function_traits` here
}
else if constexpr(std::is_callable<T(any_type)>{})
{
    return deduced_arity::unary;
}
else if constexpr(/* user manually marked arity */)
{
    /* deal with user-defined deduction helpers */
}
else
{
    return deduced_arity::undeducible;
}

重复使用 中的大部分代码(关于查找函数的元数的代码):

template <class, std::size_t N, class = std::make_index_sequence<N>, class = void_t<>>
struct CanCall : std::false_type { };

template <class F, std::size_t N, std::size_t... Idx>
struct CanCall<
    F, N,
    std::index_sequence<Idx...>,
    void_t<decltype(std::declval<F>()((Idx, std::declval<Any const&&>())...))>
> : std::true_type { };

CanCall<F, N> 将 return F 是否可以使用 N 任意类型的参数调用。 Any 帮助器类型具有模板化的隐式转换运算符,允许它变形为任何所需的参数类型。

然后,对于您的特定 use-case(一元函子):

template <class F>
struct IsUnaryFunctor : CanCall<F, 1u> { };

See it live on Coliru