averiguar si un objeto C ++ es invocable

¿Es posible escribir un rasgo de tipo, digamos is_callable que indica si un objeto tiene un operator() definido? Es fácil si los argumentos para el operador de llamada se conocen de antemano, pero no en el caso general. Quiero que el rasgo vuelva verdadero si y solo si hay al menos un operador de llamada sobrecargado definido.

Esta pregunta está relacionada y tiene una buena respuesta, pero no funciona en todos los tipos (solo int convertible). Además, std::is_function funciona pero solo en funciones reales y no en funtores, estoy buscando una solución más general.

Creo que este rasgo hace lo que quieres. Detecta operator() con cualquier tipo de firma incluso si está sobrecargado y también si está templado:

 template struct is_callable { private: typedef char(&yes)[1]; typedef char(&no)[2]; struct Fallback { void operator()(); }; struct Derived : T, Fallback { }; template struct Check; template static yes test(...); template static no test(Check*); public: static const bool value = sizeof(test(0)) == sizeof(yes); }; 

El principio se basa en el modismo de Member Detector . Tal como están las cosas, no podrá comstackrse si le pasa un tipo que no es de clase, pero eso no debería ser difícil de solucionar, simplemente lo dejé por brevedad. También puede extenderlo para informar verdadero para las funciones.

Por supuesto, no le da ninguna información sobre la (s) firma (s) de operator() absoluto, pero creo que eso no es lo que usted pidió, ¿verdad?

EDIT para Klaim :

Es lo suficientemente simple como para hacer que funcione (devuelve false ) con tipos que no son de clase. Si cambia el nombre de la clase anterior a is_callable_impl , puede escribir esto, por ejemplo:

 template struct is_callable : std::conditional< std::is_class::value, is_callable_impl, std::false_type >::type { }; 

Aquí hay una posible solución usando C ++ 11 que funciona sin necesidad de conocer la firma del operador de llamada para los funtores, pero solo mientras el functor no tenga más de una sobrecarga de operator () :

 #include  template struct is_callable : std::is_function { }; template struct is_callable::value >::type> : std::true_type { }; 

Así es como lo usarías:

 struct C { void operator () () { } }; struct NC { }; struct D { void operator () () { } void operator () (int) { } }; int main() { static_assert(is_callable::value, "Error"); static_assert(is_callable::value, "Error"); auto l = [] () { }; static_assert(is_callable::value, "Error"); // Fires! (no operator()) static_assert(is_callable::value, "Error"); // Fires! (several overloads of operator ()) static_assert(is_callable::value, "Error"); } 

Aquí hay un ejemplo en vivo .

Las respuestas aquí fueron útiles, pero vine aquí buscando algo que también pudiera detectar si algo era invocable, independientemente de si se trataba de un objeto o una función clásica. La respuesta de jrok a este aspecto del problema, ¡ay !, no funcionó porque std::conditional realmente evalúa los tipos de ambos arms!

Entonces, aquí hay una solución:

 // Note that std::is_function says that pointers to functions // and references to functions aren't functions, so we'll make our // own is_function_t that pulls off any pointer/reference first. template using remove_ref_t = typename std::remove_reference::type; template using remove_refptr_t = typename std::remove_pointer>::type; template using is_function_t = typename std::is_function>::type; // We can't use std::conditional because it (apparently) must determine // the types of both arms of the condition, so we do it directly. // Non-objects are callable only if they are functions. template struct is_callable_impl : public is_function_t {}; // Objects are callable if they have an operator(). We use a method check // to find out. template struct is_callable_impl { private: struct Fallback { void operator()(); }; struct Derived : T, Fallback { }; template struct Check; template static std::true_type test(...); template static std::false_type test(Check*); public: typedef decltype(test(nullptr)) type; }; // Now we have our final version of is_callable_t. Again, we have to take // care with references because std::is_class says "No" if we give it a // reference to a class. template using is_callable_t = typename is_callable_impl>::value, remove_ref_t >::type; 

Pero al final, para mi aplicación, realmente quería saber si se podría decir f () (es decir, llamarlo sin argumentos), así que en lugar de eso fui con algo mucho más simple.

 template  constexpr bool noarg_callable_impl( typename std::enable_if()(),0)))>::type*) { return true; } template constexpr bool noarg_callable_impl(...) { return false; } template constexpr bool is_noarg_callable() { return noarg_callable_impl(nullptr); } 

De hecho, fui aún más lejos. Sabía que se suponía que la función debía devolver un int , así que en lugar de simplemente comprobar que podía llamarlo, también verifiqué el tipo de devolución cambiando el enable_if a:

  typename std::enable_if()()), int>::value>::type*) 

¡Espero que esto ayude a alguien!

Nota: Suponen que el constructor predeterminado es válido para el tipo que marca. No estoy seguro de cómo hacerlo.

Lo siguiente parece funcionar si se puede llamar con 0 argumentos. ¿Hay algo en la implementación de is_function que pueda ayudar a extender esto a 1 o más argumento callables ?:

 template  struct is_callable { // Types "yes" and "no" are guaranteed to have different sizes, // specifically sizeof(yes) == 1 and sizeof(no) == 2. typedef char yes[1]; typedef char no[2]; template  static yes& test(decltype(C()())*); template  static no& test(...); // If the "sizeof" the result of calling test(0) would be equal to the sizeof(yes), // the first overload worked and T has a nested type named foobar. static const bool value = sizeof(test(0)) == sizeof(yes); }; 

Si conoce el tipo de argumento (incluso si es un parámetro de plantilla), lo siguiente funcionaría para 1 argumento, e imagino que uno podría extenderse muy fácilmente desde allí:

 template  struct is_callable_1 { // Types "yes" and "no" are guaranteed to have different sizes, // specifically sizeof(yes) == 1 and sizeof(no) == 2. typedef char yes[1]; typedef char no[2]; template  static yes& test(decltype(C()(T2()))*); template  static no& test(...); // If the "sizeof" the result of calling test(0) would be equal to the sizeof(yes), // the first overload worked and T has a nested type named foobar. static const bool value = sizeof(test(0)) == sizeof(yes); }; 

Editar aquí es una modificación que maneja el caso donde el constructor predeterminado no está disponible.

Hay varias otras respuestas, por supuesto, y son útiles, pero ninguna de ellas cubre cada caso de uso AFAICT. Tomando prestado de esas respuestas y esta posible implementación de std :: is_function , creé una versión que cubre todos los casos de uso posibles de los que podría pensar. Es un poco largo, pero muy completo ( Demo ).

 template struct is_callable { static bool const constexpr value = std::conditional_t< std::is_class>::value, is_callable, int>, std::false_type>::value; }; template struct is_callable : std::true_type {}; template struct is_callable : std::true_type {}; template struct is_callable : std::true_type {}; template struct is_callable : std::true_type {}; template struct is_callable : std::true_type {}; template struct is_callable : std::true_type {}; template struct is_callable : std::true_type {}; template struct is_callable : std::true_type {}; template struct is_callable : std::true_type {}; template struct is_callable : std::true_type {}; template struct is_callable : std::true_type{}; template struct is_callable : std::true_type {}; template struct is_callable : std::true_type {}; template struct is_callable : std::true_type{}; template struct is_callable : std::true_type{}; template struct is_callable : std::true_type{}; template struct is_callable : std::true_type {}; template struct is_callable : std::true_type{}; template struct is_callable : std::true_type{}; template struct is_callable : std::true_type{}; template struct is_callable : std::true_type{}; template struct is_callable : std::true_type{}; template struct is_callable : std::true_type{}; template struct is_callable : std::true_type{}; template struct is_callable : std::true_type{}; template struct is_callable : std::true_type{}; template struct is_callable : std::true_type{}; template struct is_callable : std::true_type{}; template struct is_callable { private: using YesType = char(&)[1]; using NoType = char(&)[2]; struct Fallback { void operator()(); }; struct Derived : T, Fallback {}; template struct Check; template static YesType Test(...); template static NoType Test(Check*); public: static bool const constexpr value = sizeof(Test(0)) == sizeof(YesType); }; 

Esto funciona correctamente con tipos que no son de clase (devuelve falso, por supuesto), tipos de función (, tipos de puntero de función, tipos de referencia de función, tipos de clase de functor, expresiones de enlace, tipos de lambda, etc. Esto funciona correctamente incluso si el constructor de clase es privado y / o no predeterminado, e incluso si el operador () está sobrecargado. Esto devuelve falso para punteros de función de miembro por diseño porque no se pueden llamar, pero puede usar bind para crear una expresión invocable.

Aquí hay otra implementación.

Hace uso de la plantilla std::is_function para funciones gratuitas.

Para las clases, usa algo similar a Member Detector Idiom . Si T tiene un operador de llamada, callable_2 contendrá más de un operator() . Esto hará que la primera función can_call sea ​​descartada (a través de SFINAE) debido a una falla de ambigüedad en decltype(&callable_2::operator()) y la segunda función can_call devolverá true . Si T no tiene un operador de llamada, se utilizará la primera función can_call (debido a las reglas de resolución de sobrecarga).

 namespace impl { struct callable_1 { void operator()(); }; template struct callable_2 : T, callable_1 { }; template static constexpr bool can_call(decltype(&callable_2::operator())*) noexcept { return false; } template static constexpr bool can_call(...) noexcept { return true; } template struct is_callable : public std::is_function { }; template struct is_callable : public is_callable { }; template struct is_callable : public is_callable { }; template struct is_callable : public is_callable { }; template struct is_callable : public is_callable { }; template struct is_callable : public std::integral_constant(0)> { }; } template using is_callable = impl::is_callable>::value, std::remove_reference_t>;