Проверьте, является ли тип функтором, включающим универсальную лямбду

Могу ли я написать метафункцию признака, чтобы выяснить, является ли тип функтором или нет?
Есть тонны кода, которые могут проверить функтор с помощью 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;
};

Однако это не работает для общей лямбды, потому что общая лямбда
«s operator() это шаблонная функция

Есть некоторый код для ограниченного случая для универсальной лямбда-версии, который накладывает некоторые ограничения на тип аргумента универсальной лямбда-выражения. Например, ответ здесь (https://stackoverflow.com/a/5117641/2580815) не будет работать, если лямбда-выражение содержит любое выражение, которое не может быть допустимым для int тип, такой как операция доступа члена.

Мне не нужна общность для арности. На самом деле, мне нужно только знать, что тип может быть функтором, который принимает только один параметр.

Как я могу реализовать свой is_functor?

Случай использования:

Я пытаюсь проверить, является ли данный параметр функтором или нет для функции шаблона, то есть я хочу, чтобы мне понравились некоторые перегруженные функции tempalte, например:

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; }; }

2

Решение

Там нет правильного способа сделать это (по крайней мере, пока мы не получим статическое отражение). Лучшее, что вы можете сделать, это проверить, что объект подлежащий выкупу с определенной степенью уверенности:

  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;
}
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 вызывается с N параметры произвольного типа. Вспомогательный тип Any имеет шаблонные операторы неявного преобразования, что позволяет ему преобразовываться в любой желаемый тип параметра.

Тогда для вашего конкретного варианта использования (унарные функторы):

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

Смотрите это в прямом эфире на Coliru

0

По вопросам рекламы [email protected]