SFINAE para verificar funciones heredadas de miembros

Usando SFINAE, puedo detectar si una clase determinada tiene una función miembro determinada. Pero, ¿y si quiero probar las funciones heredadas de miembros?

Lo siguiente no funciona en VC8 y GCC4 (es decir, detecta que A tiene una función miembro foo() , pero no que B hereda una):

 #include  template struct has_foo { template  struct type_check; template  static char (& chk(type_check*))[1]; template  static char (& chk(...))[2]; static bool const value = (sizeof(chk(0)) == 1); }; struct A { void foo(); }; struct B : A {}; int main() { using namespace std; cout << boolalpha << has_foo::value << endl; // true cout << boolalpha << has_foo::value << endl; // false } 

Entonces, ¿hay alguna forma de probar las funciones heredadas de los miembros?

Echa un vistazo a este hilo:

http://lists.boost.org/boost-users/2009/01/44538.php

Derivado del código vinculado a esa discusión:

 #include  template  class has_foo { class yes { char m;}; class no { yes m[2];}; struct BaseMixin { void foo(){} }; struct Base : public Type, public BaseMixin {}; template  class Helper{}; template  static no deduce(U*, Helper* = 0); static yes deduce(...); public: static const bool result = sizeof(yes) == sizeof(deduce((Base*)(0))); }; struct A { void foo(); }; struct B : A {}; struct C {}; int main() { using namespace std; cout < < boolalpha << has_foo::result < < endl; cout << boolalpha << has_foo::result < < endl; cout << boolalpha << has_foo::result; } 

Resultado:

 true true false 

La respuesta de joshperry es muy inteligente y elegante, pero (como se indica debajo de la publicación) no comprueba correctamente la firma de foo () y no funciona con tipos fundamentales (como int): causa un error de comstackción. Propondré una técnica que maneja correctamente los miembros heredados y también verifica la firma de la función miembro. En lugar de entrar en detalles, le daré dos ejemplos y espero que el código hable por sí mismo.

Ejemplo 1:

Estamos buscando un miembro con la siguiente firma: T::const_iterator begin() const

 template struct has_const_begin { typedef char (&Yes)[1]; typedef char (&No)[2]; template static Yes test(U const * data, typename std::enable_ifbegin()) >::value>::type * = 0); static No test(...); static const bool value = sizeof(Yes) == sizeof(has_const_begin::test((typename std::remove_reference::type*)0)); }; 

Tenga en cuenta que incluso comprueba la consistencia del método y también funciona con tipos primitivos. (Me refiero a que has_const_begin::value es falso y no causa un error en tiempo de comstackción).

Ejemplo 2

Ahora estamos buscando la firma: void foo(MyClass&, unsigned)

 template struct has_foo { typedef char (&Yes)[1]; typedef char (&No)[2]; template static Yes test(U * data, MyClass* arg1 = 0, typename std::enable_iffoo(*arg1, 1u)) >::value>::type * = 0); static No test(...); static const bool value = sizeof(Yes) == sizeof(has_foo::test((typename std::remove_reference::type*)0)); }; 

Tenga en cuenta que MyClass no tiene que ser necesariamente construible por defecto o para satisfacer ningún concepto especial. La técnica también funciona con los miembros de la plantilla.

Estoy esperando ansiosamente opiniones con respecto a esto.

Aquí hay algunos fragmentos de uso: * Las agallas de todo esto están más abajo

Verifique el miembro x en una clase determinada. Podría ser var, func, class, union o enum:

 CREATE_MEMBER_CHECK(x); bool has_x = has_member_x::value; 

Verificar la función miembro void x() :

 //Func signature MUST have T as template variable here... simpler this way :\ CREATE_MEMBER_FUNC_SIG_CHECK(x, void (T::*)(), void__x); bool has_func_sig_void__x = has_member_func_void__x::value; 

Verifique la variable de miembro x :

 CREATE_MEMBER_VAR_CHECK(x); bool has_var_x = has_member_var_x::value; 

Verifique la clase de miembro x :

 CREATE_MEMBER_CLASS_CHECK(x); bool has_class_x = has_member_class_x::value; 

Verifique la unión de miembros x :

 CREATE_MEMBER_UNION_CHECK(x); bool has_union_x = has_member_union_x::value; 

Verifique la enumeración del miembro x :

 CREATE_MEMBER_ENUM_CHECK(x); bool has_enum_x = has_member_enum_x::value; 

Verifique cualquier función miembro x independientemente de la firma:

 CREATE_MEMBER_CHECK(x); CREATE_MEMBER_VAR_CHECK(x); CREATE_MEMBER_CLASS_CHECK(x); CREATE_MEMBER_UNION_CHECK(x); CREATE_MEMBER_ENUM_CHECK(x); CREATE_MEMBER_FUNC_CHECK(x); bool has_any_func_x = has_member_func_x::value; 

O

 CREATE_MEMBER_CHECKS(x); //Just stamps out the same macro calls as above. bool has_any_func_x = has_member_func_x::value; 

Detalles y núcleo:

 /* - Multiple inheritance forces ambiguity of member names. - SFINAE is used to make aliases to member names. - Expression SFINAE is used in just one generic has_member that can accept any alias we pass it. */ //Variadic to force ambiguity of class members. C++11 and up. template  struct ambiguate : public Args... {}; //Non-variadic version of the line above. //template  struct ambiguate : public A, public B {}; template struct got_type : std::false_type {}; template struct got_type : std::true_type { typedef A type; }; template struct sig_check : std::true_type {}; template struct has_member { template static char ((&f(decltype(&C::value))))[1]; template static char ((&f(...)))[2]; //Make sure the member name is consistently spelled the same. static_assert( (sizeof(f(0)) == 1) , "Member name specified in AmbiguitySeed is different from member name specified in Alias, or wrong Alias/AmbiguitySeed has been specified." ); static bool const value = sizeof(f(0)) == 2; }; 

Macros (El Diablo!):

CREATE_MEMBER_CHECK:

 //Check for any member with given name, whether var, func, class, union, enum. #define CREATE_MEMBER_CHECK(member) \ \ template \ struct Alias_##member; \ \ template \ struct Alias_##member < \ T, std::integral_constant::value> \ > { static const decltype(&T::member) value; }; \ \ struct AmbiguitySeed_##member { char member; }; \ \ template \ struct has_member_##member { \ static const bool value \ = has_member< \ Alias_##member> \ , Alias_##member \ >::value \ ; \ } 

CREATE_MEMBER_VAR_CHECK:

 //Check for member variable with given name. #define CREATE_MEMBER_VAR_CHECK(var_name) \ \ template \ struct has_member_var_##var_name : std::false_type {}; \ \ template \ struct has_member_var_##var_name< \ T \ , std::integral_constant< \ bool \ , !std::is_member_function_pointer::value \ > \ > : std::true_type {} 

CREATE_MEMBER_FUNC_SIG_CHECK:

 //Check for member function with given name AND signature. #define CREATE_MEMBER_FUNC_SIG_CHECK(func_name, func_sig, templ_postfix) \ \ template \ struct has_member_func_##templ_postfix : std::false_type {}; \ \ template \ struct has_member_func_##templ_postfix< \ T, std::integral_constant< \ bool \ , sig_check::value \ > \ > : std::true_type {} 

CREATE_MEMBER_CLASS_CHECK:

 //Check for member class with given name. #define CREATE_MEMBER_CLASS_CHECK(class_name) \ \ template \ struct has_member_class_##class_name : std::false_type {}; \ \ template \ struct has_member_class_##class_name< \ T \ , std::integral_constant< \ bool \ , std::is_class< \ typename got_type::type \ >::value \ > \ > : std::true_type {} 

CREATE_MEMBER_UNION_CHECK:

 //Check for member union with given name. #define CREATE_MEMBER_UNION_CHECK(union_name) \ \ template \ struct has_member_union_##union_name : std::false_type {}; \ \ template \ struct has_member_union_##union_name< \ T \ , std::integral_constant< \ bool \ , std::is_union< \ typename got_type::type \ >::value \ > \ > : std::true_type {} 

CREATE_MEMBER_ENUM_CHECK:

 //Check for member enum with given name. #define CREATE_MEMBER_ENUM_CHECK(enum_name) \ \ template \ struct has_member_enum_##enum_name : std::false_type {}; \ \ template \ struct has_member_enum_##enum_name< \ T \ , std::integral_constant< \ bool \ , std::is_enum< \ typename got_type::type \ >::value \ > \ > : std::true_type {} 

CREATE_MEMBER_FUNC_CHECK:

 //Check for function with given name, any signature. #define CREATE_MEMBER_FUNC_CHECK(func) \ template \ struct has_member_func_##func { \ static const bool value \ = has_member_##func::value \ && !has_member_var_##func::value \ && !has_member_class_##func::value \ && !has_member_union_##func::value \ && !has_member_enum_##func::value \ ; \ } 

CREATE_MEMBER_CHECKS:

 //Create all the checks for one member. Does NOT include func sig checks. #define CREATE_MEMBER_CHECKS(member) \ CREATE_MEMBER_CHECK(member); \ CREATE_MEMBER_VAR_CHECK(member); \ CREATE_MEMBER_CLASS_CHECK(member); \ CREATE_MEMBER_UNION_CHECK(member); \ CREATE_MEMBER_ENUM_CHECK(member); \ CREATE_MEMBER_FUNC_CHECK(member) 

Como todas las respuestas me parecen demasiado complicadas, me gustaría presentar mi propia solución usando std::declval y std::enable_if (GCC 4.8.3)

 #define MEMBER_FUNC_CHECKER(name, fn, ret, args) \ template struct name : std::false_type {}; \ template struct name().fn args), ret \ >::value>::type> : std::true_type {}; 

NOTA: No se trata de una comprobación precisa de la firma, sino de una función invocable con un tipo de devolución convertible. (editar: cambiado de is_same a is_convertible )

Prueba

 struct One { int get() { return 0; } int add(int x, int y) { return x+y; } }; struct Two: One {}; struct Not {}; MEMBER_FUNC_CHECKER(has_get, get, int, ()) MEMBER_FUNC_CHECKER(has_add, add, int, (1,2)) int main() { cout < < "One " << (has_get() ? "has" : "does not have") < < " int get()" << endl; cout << "Two " << (has_get() ? "has" : "does not have") < < " int get()" << endl; cout << "Not " << (has_get() ? "has" : "does not have") < < " int get()" << endl; cout << "One " << (has_add() ? "has" : "does not have") < < " int add(int, int)" << endl; cout << "Two " << (has_add() ? "has" : "does not have") < < " int add(int, int)" << endl; cout << "Not " << (has_add() ? "has" : "does not have") < < " int add(int, int)" << endl; cout << "int " << (has_get() ? "has" : "does not have") < < " int get()" << endl; } 

Salida

 Uno tiene int get ()
 Dos tiene int get ()
 No no tiene int get ()
 Uno tiene int add (int, int)
 Two tiene int add (int, int)
 No tiene int add (int, int)
 int no tiene int get ()

ACTUALIZACIÓN: Mis damas

 /// Checker for typedef with given name and convertible type #define TYPEDEF_CHECKER(checker, name) \ template struct checker : std::false_type {}; \ template struct checker::value>::type> : std::true_type {} /// Checker for typedef with given name and exact type #define TYPEDEF_CHECKER_STRICT(checker, name) \ template struct checker : std::false_type {}; \ template struct checker::value>::type> : std::true_type {} /// Checker for typedef with given name and any type #define TYPEDEF_CHECKER_ANY(checker, name) \ template struct checker : std::false_type {}; \ template struct checker::value>::type> : std::true_type {} /// Checker for member with given name and convertible type #define MTYPE_CHECKER(checker, name) \ template struct checker : std::false_type {}; \ template struct checker::value>::type> : std::true_type {} /// Checker for member with given name and exact type #define MTYPE_CHECKER_STRICT(checker, name) \ template struct checker : std::false_type {}; \ template struct checker::value>::type> : std::true_type {} /// Checker for member with given name and any type #define MTYPE_CHECKER_ANY(checker, name) \ template struct checker : std::false_type {}; \ template struct checker::value>::type> : std::true_type {} /// Checker for static const variable with given name and value #define MVALUE_CHECKER(checker, name, val) \ template struct checker : std::false_type {}; \ template struct checker::value && C::name == val>::type> : std::true_type {} /// Checker for static const variable with given name, value and type #define MVALUE_CHECKER_STRICT(checker, name, val) \ template struct checker : std::false_type {}; \ template struct checker::value && C::name == val>::type> : std::true_type {} /// Checker for member function with convertible return type and accepting given arguments #define METHOD_CHECKER(checker, name, ret, args) \ template struct checker : std::false_type {}; \ template struct checker().name args), ret>::value>::type> : std::true_type {}; /// Checker for member function with exact retutn type and accepting given arguments #define METHOD_CHECKER_STRICT_RET(name, fn, ret, args) \ template struct name : std::false_type {}; \ template struct name().fn args), ret>::value>::type> : std::true_type {}; /// Checker for member function accepting given arguments #define METHOD_CHECKER_ANY(name, fn, args) \ template struct name : std::false_type {}; \ template struct name().fn args)*, void>::value>::type> : std::true_type {}; 

Código de prueba

 struct One { typedef int type; static constexpr bool v = true; type x; One(type x = 0): x(x) {} ~One() {} type get() { return x; } type add(type x, type y) { return x+y; } }; struct Two: One {}; struct Not {}; TYPEDEF_CHECKER(has_type, type); TYPEDEF_CHECKER_ANY(any_type, type); TYPEDEF_CHECKER_STRICT(exact_type, type); MTYPE_CHECKER(has_x, x); MTYPE_CHECKER_ANY(any_x, x); MTYPE_CHECKER_STRICT(exact_x, x); MVALUE_CHECKER(true_v, v, true); MVALUE_CHECKER(true_z, z, true); MVALUE_CHECKER(false_v, v, false); MVALUE_CHECKER(one_v, v, 1); MVALUE_CHECKER_STRICT(exact_v, v, 1); METHOD_CHECKER(has_get, get, long, ()); METHOD_CHECKER(has_add, add, long, (1,2)) METHOD_CHECKER_ANY(any_get, get, ()); METHOD_CHECKER_STRICT_RET(int_get, get, int, ()) METHOD_CHECKER_STRICT_RET(long_get, get, long, ()) int main() { #define CHECK(name, desc, ...) cout < < endl; \ cout << "One " << (name() ? "has " : "does not have ") < < desc << endl; \ cout << "Two " << (name() ? "has " : "does not have ") < < desc << endl; \ cout << "Not " << (name() ? "has " : "does not have ") < < desc << endl; \ cout << "int " << (name() ? "has " : "does not have ") < < desc << endl string sep = string(60, '-'); cout << sep; CHECK(any_type, "typedef type"); CHECK(has_type, "typedef type convertible to long", long); CHECK(exact_type, "typedef type = int", int); CHECK(exact_type, "typedef type = long", long); cout << sep; CHECK(any_x, "var x"); CHECK(has_x, "var x of type convertible to long", long); CHECK(exact_x, "var x of type int", int); CHECK(exact_x, "var x of type long", long); cout << sep; CHECK(true_v, "var v with value equal to true"); CHECK(true_z, "var z with value equal to true"); CHECK(false_v, "var v with value equal to false"); CHECK(one_v, "var v with value equal to 1"); CHECK(exact_v, "var v with value equal to 1 of type int"); cout << sep; CHECK(has_get, "get()"); CHECK(has_get, "get() with return type covertible to long"); CHECK(has_add, "add() accepting two ints and returning ~ long"); CHECK(int_get, "int get()"); CHECK(long_get, "long get()"); } 

Salida

 Uno tiene tipo typedef
 Dos tienen tipo typedef
 No no tiene typedef type
 int no tiene typedef type

 Uno tiene tipo typedef convertible a largo
 Dos tienen tipo typedef convertible a largo
 No tiene typedef type convertible to long
 int no tiene typedef type convertible to long

 Uno tiene typedef type = int
 Dos tienen typedef type = int
 No tiene typedef type = int
 int no tiene typedef type = int

 Uno no tiene typedef type = long
 Dos no tienen typedef type = long
 No tiene typedef type = long
 int no tiene typedef type = long
 -------------------------------------------------- ----------
 Uno tiene var x
 Dos tiene var x
 No no tiene var x
 int no tiene var x

 Uno tiene var x del tipo convertible a long
 Dos tienen var x del tipo convertible a long
 No tiene var x del tipo convertible a long
 int no tiene var x del tipo convertible a long

 Uno tiene var x de tipo int
 Two tiene var x de tipo int
 No no tiene var x de tipo int
 int no tiene var x de tipo int

 Uno no tiene var x de tipo long
 Dos no tiene var x de tipo long
 No tiene var x de tipo long
 int no tiene var x de tipo long
 -------------------------------------------------- ----------
 Uno tiene var v con valor igual a verdadero
 Two tiene var v con valor igual a true
 No no tiene var v con valor igual a verdadero
 int no tiene var v con valor igual a true

 Uno no tiene var z con valor igual a verdadero
 Two no tiene var z con valor igual a true
 No no tiene var z con valor igual a verdadero
 int no tiene var z con valor igual a true

 Uno no tiene var v con valor igual a falso
 Dos no tiene var v con valor igual a falso
 No no tiene var v con valor igual a falso
 int no tiene var v con valor igual a falso

 Uno tiene var v con valor igual a 1
 Dos tienen var v con valor igual a 1
 No no tiene var v con valor igual a 1
 int no tiene var v con valor igual a 1

 Uno no tiene var v con valor igual a 1 de tipo int
 Two no tiene var v con valor igual a 1 de tipo int
 No no tiene var v con valor igual a 1 de tipo int
 int no tiene var v con valor igual a 1 de tipo int
 -------------------------------------------------- ----------
 Uno tiene get ()
 Dos tiene get ()
 No no tiene get ()
 int no tiene get ()

 Uno tiene get () con tipo de retorno covertible a largo
 Dos tienen get () con tipo de retorno covertible a largo
 No tiene get () con tipo de retorno covertible a largo
 int no tiene get () con tipo de retorno covertible a largo

 Uno tiene add () aceptando dos ints y regresando ~ long
 Dos tiene add () aceptando dos ints y devolviendo ~ long
 No tiene add () aceptando dos ints y devolviendo ~ long
 int no tiene add () aceptando dos ints y devolviendo ~ long

 Uno tiene int get ()
 Dos tiene int get ()
 No no tiene int get ()
 int no tiene int get ()

 Uno no tiene mucho get ()
 Dos no tiene mucho get ()
 No tiene mucho get ()
 int no tiene long get ()