2012-01-31 3 views
12

Как я могу вывести статически, если аргумент является объектом функции C++ (функтор)?Возможно ли класс признака is_functor C++?

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

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

+0

как насчет 'is_function :: value'? – Fiktik

+1

http://groups.google.com/group/comp.lang.c++.moderated/msg/e5fbc9305539f699 может заинтересовать вас. – pmr

+1

Вы просто хотите проверить для функторов или для любого вызываемого объекта? Похоже, что использование SFINAE признака 'result_of' будет работать, чтобы идентифицировать любой вызываемый тип. Я немного удивлен тем, что уже не существует какой-либо «std :: is_callable». – bames53

ответ

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

изменено с this answer.

Он может быть использован как ...

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

'typename decltype'? И ваше решение не работает, если 'operator()' перегружен. – kennytm

+0

Ваше определение функтора является неполным. Стандартный функтор - это либо указатель на функцию, либо объект с перегруженным 'operator()'. –

+0

Я разместил другое решение; @MaximYegorushkin, но новый не меняется относительно этого, hmmm – David

12

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

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

Шаг один: Бесплатные функции

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

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

Шаг три: Ввод частей вместе

Мы почти закончили. Теперь нам нужно только решить, когда использовать нашу свободную функцию, и когда метафункции класса функций. К счастью, C++ 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

+0

Wow.Wow.Wow.Wow.Wow. – mark

0

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

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

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

Смежные вопросы