C ++ metafunction, чтобы определить, является ли тип вызываемым

Возможно ли написать мета-функцию C ++ (0x), которая определяет, является ли тип вызываемым?

Под вызываемым типом я подразумеваю тип функции, тип указателя функции, тип ссылочного типа (они определяются методом boost::function_types::is_callable_builtin ), типы lambda и любой class с перегруженным operator() (и, возможно, любой class с неявным оператор преобразования к одному из них, но это не обязательно).

EDIT : Metafunction должен обнаруживать присутствие operator() с любой подписью, включая шаблонный operator() . Я считаю, что это сложная часть.

EDIT : Вот пример использования:

 template  struct and_predicate { template  bool operator()(const ArgT& arg) { return predicate1(arg) && predicate2(arg); } Predicate1 predicate1; Predicate2 predicate2; }; template  enable_if<ice_and<is_callable::value, is_callable::value>::value, and_predicate>::type operator&&(Predicate1 predicate1, Predicate2 predicate2) { return and_predicate{predicate1, predicate2}; } 

is_callable – это то, что я хотел бы реализовать.

Наличие неэмпированного T :: operator () для заданного типа T может быть обнаружено:

 template // detect regular operator() static char test(decltype(&C::operator())); template // worst match static char (&test(...))[2]; static const bool value = (sizeof( test(0) ) 

Наличие шаблонизированного оператора можно обнаружить:

 template // detect 1-arg operator() static char test(int, decltype( (*(F*)0)( (*(A*)0) ) ) = 0); template // detect 2-arg operator() static char test(int, decltype( (*(F*)0)( (*(A*)0), (*(B*)0) ) ) = 0); // ... detect N-arg operator() template // worst match static char (&test(...))[2]; static const bool value = (sizeof( test(0) ) == 1) || (sizeof( test(0) ) == 1); // etc... 

Однако эти два не играют хорошо вместе, так как decltype (& C :: operator ()) выдает ошибку, если C имеет шаблонный оператор вызова функции. Решение состоит в том, чтобы сначала выполнить последовательность проверок против шаблонного оператора и проверить регулярный оператор (), если и только если шаблонный не может быть найден. Это делается, специализируясь на не templated check для no-op, если шаблон был найден.

 template struct has_regular_call_operator { template // detect regular operator() static char test(decltype(&C::operator())); template // worst match static char (&test(...))[2]; static const bool value = (sizeof( test(0) ) == 1); }; template struct has_regular_call_operator { static const bool value = true; }; template struct has_call_operator { template // detect 1-arg operator() static char test(int, decltype( (*(F*)0)( (*(A*)0) ) ) = 0); template // detect 2-arg operator() static char test(int, decltype( (*(F*)0)( (*(A*)0), (*(B*)0) ) ) = 0); template // detect 3-arg operator() static char test(int, decltype( (*(F*)0)( (*(A*)0), (*(B*)0), (*(C*)0) ) ) = 0); template // worst match static char (&test(...))[2]; static const bool OneArg = (sizeof( test(0) ) == 1); static const bool TwoArg = (sizeof( test(0) ) == 1); static const bool ThreeArg = (sizeof( test(0) ) == 1); static const bool HasTemplatedOperator = OneArg || TwoArg || ThreeArg; static const bool value = has_regular_call_operator::value; }; 

Если арность всегда одна, как обсуждалось выше, тогда проверка должна быть проще. Я не вижу необходимости в каких-либо дополнительных типах или библиотечных возможностях для этого.

С появлением нашего коллективного опыта в c ++ 11 (и за его пределами), вероятно, пора пересмотреть этот вопрос.

Кажется, что эта маленькая черта характерна для меня:

 #include  #include  template struct is_callable { template static auto test(U* p) -> decltype((*p)(std::declval()...), void(), std::true_type()); template static auto test(...) -> decltype(std::false_type()); static constexpr bool value = decltype(test(0))::value; }; 

Что мы можем проверить таким образом:

 template::value>::type* = nullptr> void test_call(F, Args&&...args) { std::cout << "callable" << std::endl; } template::value>::type* = nullptr> void test_call(F, Args&&...args) { std::cout << "not callable" << std::endl; } extern void f3(int, const std::string&) { } int main() { auto f1 = [](int, std::string) {}; test_call(f1, 0, "hello"); test_call(f1, "bad", "hello"); std::function f2; test_call(f2, 0, "hello"); test_call(f2, "bad", "hello"); test_call(f3, 0, "hello"); test_call(f3, "bad", "hello"); } 

ожидаемый результат:

 callable not callable callable not callable callable not callable 

Это действительно интересный вопрос. Я был озадачен этим.

Я думаю, что мне удалось внести изменения в код Crazy Eddie, который позволит любое количество параметров, однако он использует вариационные шаблоны, и для этого требуется указать параметры, которые вы ожидаете от вызываемого объекта, который должен быть вызван. Короче говоря, я получил эту работу и работал, как ожидалось, на gcc 4.6.0:

EDIT: Можно также использовать утилиту std :: result_of, однако она не работает, потому что для нее требуется typename чтобы разузнавать std::result_of<..>::type который разбивает Sfinae.

 #include  #include  template < typename PotentiallyCallable, typename... Args> struct is_callable { typedef char (&no) [1]; typedef char (&yes) [2]; template < typename T > struct dummy; template < typename CheckType> static yes check(dummy()(std::declval()...))> *); template < typename CheckType> static no check(...); enum { value = sizeof(check(0)) == sizeof(yes) }; }; int f1(int,double) { return 0; }; typedef int(*f1_type)(int,double) ; //this is just to have a type to feed the template. struct Foo { }; struct Bar { template  void operator()(T) { }; }; int main() { if( is_callable::value ) std::cout << "f1 is callable!" << std::endl; if( is_callable::value ) std::cout << "Foo is callable!" << std::endl; if( is_callable::value ) std::cout << "Bar is callable with int!" << std::endl; if( is_callable::value ) std::cout << "Bar is callable with double!" << std::endl; }; 

Надеюсь, это то, что вы ищете, потому что я не думаю, что можно сделать гораздо больше.

EDIT: для вашего варианта использования это частичное решение, но оно может помочь:

 template  struct and_predicate { template  enable_if::value, is_callable::value>::value, bool>::type operator()(const ArgT& arg) { return predicate1(arg) && predicate2(arg); } Predicate1 predicate1; Predicate2 predicate2; }; template  enable_if::value, is_callable< Predicate2, boost::any >::value>::value, and_predicate>::type operator&&(Predicate1 predicate1, Predicate2 predicate2) { return and_predicate{predicate1, predicate2}; } 
Давайте будем гением компьютера.