2

我可以编写一个特征元函数来确定一个类型是否是函子吗?有大量的代码可以使用 SFINAE 来检查函子decltype(&T::operator()),例如,

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,因为泛型 lambdaoperator()是一个模板函数。

有一些用于泛型 lambda 版本的有限情况的代码,它们对泛型 lambda 的参数类型进行了一些限制。例如,如果 lambda 表达式包含任何对成员访问操作等类型无效的表达式,则此处的答案 ( https://stackoverflow.com/a/5117641/2580815 ) 将不起作用。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; }; }
4

2 回答 2

1

没有正确的方法可以做到这一点(至少在我们得到静态反射之前)。您可以做的最好的事情是检查一个对象是否可以以一定的置信度调用:

  1. 尝试获取它的operator()地址。如果失败,则该对象可能是不可调用的,或者它operator()可能被重载/模板化。

  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静态地告诉您函数对象的确切数量的实现。这可以在 Boost中找到。

然后你还需要一个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(...) (或任何其他编译时分支机制)来做出一个很好的猜测——例如:

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;
}
于 2017-02-17T09:47:49.953 回答
1

重用我对双胞胎问题的回答中的大部分代码(关于查找函数的元数的问题):

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>将返回是否可以使用任意类型F的参数调用。NAny 帮助器类型具有模板化的隐式转换运算符,允许它变形为任何所需的参数类型。

然后,对于您的特定用例(一元函子):

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

在 Coliru 上现场观看

于 2017-02-17T09:51:07.180 回答