Подтвердить что ты не робот

Возможно ли класс признака is_functor С++?

Как я могу сделать вывод статически, если аргумент является объектом функции С++ (функтор)?

template <typename F>
void test(F f) {}

Я пробовал is_function<F>::value, но это не работает. Также кажется, что нет черты is_functor, поэтому, возможно, это невозможно. Кажется, я ищу только определенную функцию-член, в этом случае оператор вызова функции: F::operator().

4b9b3361

Ответ 1

Можно создать такой признак с двумя ограничениями:

  • Для компилятора свободная функция - это нечто принципиально отличное от функтора класса, который перегружает operator(). Таким образом, мы должны рассматривать оба случая отдельно при реализации. Это не проблема для использования, но мы можем скрыть эту деталь реализации от пользователя.
  • Нам нужно знать подпись функции, которую вы хотите вызвать. Обычно это не проблема, и у этого есть хороший побочный эффект, который наш признак способен обрабатывать перегрузки довольно изначально.

Шаг первый: свободные функции

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

// build R (*)(Args...) from R (Args...)
// compile error if signature is not a valid function signature
template <typename, typename>
struct build_free_function;

template <typename F, typename R, typename ... Args>
struct build_free_function<F, R (Args...)>
{ using type = R (*)(Args...); };

Теперь все, что осталось сделать, это сравнить, и мы закончили со свободной частью функции:

// determine whether a free function pointer F has signature S
template <typename F, typename S>
struct is_function_with_signature
{
    // check whether F and the function pointer of S are of the same
    // type
    static bool constexpr value = std::is_same<
        F, typename build_free_function<F, S>::type
    >::value;
};

Шаг второй: Функторы классов

Это немного больше. Мы могли бы легко обнаружить с помощью SFINAE, определяет ли класс operator():

template <typename T>
struct defines_functor_operator
{
    typedef char (& yes)[1];
    typedef char (& no)[2];

    // we need a template here to enable SFINAE
    template <typename U> 
    static yes deduce(char (*)[sizeof(&U::operator())]);
    // fallback
    template <typename> static no deduce(...);

    static bool constexpr value = sizeof(deduce<T>(0)) == sizeof(yes);
};

но это не говорит нам, существует ли для нашей желаемой сигнатуры функции! К счастью, мы можем использовать трюк здесь: указатели являются действительными параметрами шаблона. Таким образом, мы можем сначала использовать указатель функции-члена нашей нужной сигнатуры и проверить, есть ли &T::operator() этого типа:

template <typename T, T> struct check;

Теперь check<void (C::*)() const, &C::operator()> будет только действительным экземпляром шаблона, если C действительно имеет void C::operator()() const. Но для этого сначала нужно объединить C и подпись с указателем на функцию-член. Как мы уже видели, нам нужно беспокоиться о двух дополнительных случаях, о которых нам не нужно заботиться о бесплатных функциях: const и volatile. Кроме того, это почти то же самое:

// build R (C::*)(Args...) from R (Args...)
//       R (C::*)(Args...) const from R (Args...) const
//       R (C::*)(Args...) volatile from R (Args...) volatile
// compile error if signature is not a valid member function signature
template <typename, typename>
struct build_class_function;

template <typename C, typename R, typename ... Args>
struct build_class_function<C, R (Args...)>
{ using type = R (C::*)(Args...); };

template <typename C, typename R, typename ... Args>
struct build_class_function<C, R (Args...) const>
{ using type = R (C::*)(Args...) const; };

template <typename C, typename R, typename ... Args>
struct build_class_function<C, R (Args...) volatile>
{ using type = R (C::*)(Args...) volatile; };

Сопоставив это и наши выводы о вспомогательной структуре check, мы получим нашу проверку metafunction для объектов-функторов:

// determine whether a class C has an operator() with signature S
template <typename C, typename S>
struct is_functor_with_signature
{
    typedef char (& yes)[1];
    typedef char (& no)[2];

    // helper struct to determine that C::operator() does indeed have
    // the desired signature; &C::operator() is only of type 
    // R (C::*)(Args...) if this is true
    template <typename T, T> struct check;

    // T is needed to enable SFINAE
    template <typename T> static yes deduce(check<
        typename build_class_function<C, S>::type, &T::operator()> *);
    // fallback if check helper could not be built
    template <typename> static no deduce(...);

    static bool constexpr value = sizeof(deduce<C>(0)) == sizeof(yes);
};

Шаг третий: объединение частей

Мы почти закончили. Теперь нам нужно только решить, когда использовать нашу свободную функцию, и когда метафункции класса функций. К счастью, С++ 11 предоставляет нам свойство std::is_class, которое мы можем использовать для этого. Итак, все, что нам нужно сделать, это специализировать на логическом параметре:

// C is a class, delegate to is_functor_with_signature
template <typename C, typename S, bool>
struct is_callable_impl
    : std::integral_constant<
        bool, is_functor_with_signature<C, S>::value
      >
{};

// F is not a class, delegate to is_function_with_signature
template <typename F, typename S>
struct is_callable_impl<F, S, false>
    : std::integral_constant<
        bool, is_function_with_signature<F, S>::value
      >
{};

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

// Determine whether type Callable is callable with signature Signature.
// Compliant with functors, i.e. classes that declare operator(); and free
// function pointers: R (*)(Args...), but not R (Args...)!
template <typename Callable, typename Signature>
struct is_callable
    : is_callable_impl<
        Callable, Signature,
        std::is_class<Callable>::value
      >
{};

Теперь мы очищаем наш код, помещаем детали реализации в анонимные пространства имен, чтобы они не были доступны вне нашего файла и имели приятный is_callable.hpp для использования в нашем проекте.

Полный код

namespace // implementation detail
{
    // build R (*)(Args...) from R (Args...)
    // compile error if signature is not a valid function signature
    template <typename, typename>
    struct build_free_function;

    template <typename F, typename R, typename ... Args>
    struct build_free_function<F, R (Args...)>
    { using type = R (*)(Args...); };

    // build R (C::*)(Args...) from R (Args...)
    //       R (C::*)(Args...) const from R (Args...) const
    //       R (C::*)(Args...) volatile from R (Args...) volatile
    // compile error if signature is not a valid member function signature
    template <typename, typename>
    struct build_class_function;

    template <typename C, typename R, typename ... Args>
    struct build_class_function<C, R (Args...)>
    { using type = R (C::*)(Args...); };

    template <typename C, typename R, typename ... Args>
    struct build_class_function<C, R (Args...) const>
    { using type = R (C::*)(Args...) const; };

    template <typename C, typename R, typename ... Args>
    struct build_class_function<C, R (Args...) volatile>
    { using type = R (C::*)(Args...) volatile; };

    // determine whether a class C has an operator() with signature S
    template <typename C, typename S>
    struct is_functor_with_signature
    {
        typedef char (& yes)[1];
        typedef char (& no)[2];

        // helper struct to determine that C::operator() does indeed have
        // the desired signature; &C::operator() is only of type 
        // R (C::*)(Args...) if this is true
        template <typename T, T> struct check;

        // T is needed to enable SFINAE
        template <typename T> static yes deduce(check<
            typename build_class_function<C, S>::type, &T::operator()> *);
        // fallback if check helper could not be built
        template <typename> static no deduce(...);

        static bool constexpr value = sizeof(deduce<C>(0)) == sizeof(yes);
    };

    // determine whether a free function pointer F has signature S
    template <typename F, typename S>
    struct is_function_with_signature
    {
        // check whether F and the function pointer of S are of the same
        // type
        static bool constexpr value = std::is_same<
            F, typename build_free_function<F, S>::type
        >::value;
    };

    // C is a class, delegate to is_functor_with_signature
    template <typename C, typename S, bool>
    struct is_callable_impl
        : std::integral_constant<
            bool, is_functor_with_signature<C, S>::value
          >
    {};

    // F is not a class, delegate to is_function_with_signature
    template <typename F, typename S>
    struct is_callable_impl<F, S, false>
        : std::integral_constant<
            bool, is_function_with_signature<F, S>::value
          >
    {};
}

// Determine whether type Callable is callable with signature Signature.
// Compliant with functors, i.e. classes that declare operator(); and free
// function pointers: R (*)(Args...), but not R (Args...)!
template <typename Callable, typename Signature>
struct is_callable
    : is_callable_impl<
        Callable, Signature,
        std::is_class<Callable>::value
      >
{};

Пример Ideone с некоторыми тестами

http://ideone.com/7PWdiv

Ответ 2

template<typename T, typename Sign>                                 
struct is_functor 
{                                                                   
    typedef char yes[1];                                            
    typedef char no [2];                                            
    template <typename U, U> struct type_check;                     
    template <typename _1> static yes &chk(type_check<Sign, &_1::operator()>*);
    template <typename   > static no  &chk(...);                    
    static bool const value = sizeof(chk<T>(nullptr)) == sizeof(yes);     
};

Изменен из этот ответ.

Его можно использовать как...

template<typename T>
typename std::enable_if<is_functor<T, void(T::*)()>::value>::type func()
{
}

Ответ 3

Хотя это не работает для перегруженных функций, для всех остальных случаев (свободные функции, классы, реализующие operator() и lambdas), это короткие решения работают в С++ 11:

template <typename T, typename Signature>
struct is_callable: std::is_convertible<T,std::function<Signature>> { };

Примечание: std::is_callable будет доступен на С++ 17.