¿Es posible escribir una plantilla para verificar la existencia de una función?

¿Es posible escribir una plantilla que cambie el comportamiento dependiendo de si se define una determinada función miembro en una clase?

Aquí hay un ejemplo simple de lo que me gustaría escribir:

template std::string optionalToString(T* obj) { if (FUNCTION_EXISTS(T->toString)) return obj->toString(); else return "toString not defined"; } 

Entonces, si la class T tiene toString() definido, entonces lo usa; de lo contrario, no es así. La parte mágica que no sé cómo hacer es la parte “FUNCTION_EXISTS”.

Sí, con SFINAE puede verificar si una clase determinada proporciona un método determinado. Aquí está el código de trabajo:

 #include  struct Hello { int helloworld() { return 0; } }; struct Generic {}; // SFINAE test template  class has_helloworld { typedef char one; typedef long two; template  static one test( typeof(&C::helloworld) ) ; template  static two test(...); public: enum { value = sizeof(test(0)) == sizeof(char) }; }; int main(int argc, char *argv[]) { std::cout < < has_helloworld::value < < std::endl; std::cout << has_helloworld::value < < std::endl; return 0; } 

Acabo de probarlo con Linux y gcc 4.1 / 4.3. No sé si es portátil para otras plataformas que ejecutan comstackdores diferentes.

Esta pregunta es antigua, pero con C ++ 11 tenemos una nueva forma de verificar la existencia de una función (o la existencia de cualquier miembro que no sea de tipo, realmente), confiando de nuevo en SFINAE:

 template auto serialize_imp(std::ostream& os, T const& obj, int) -> decltype(os < < obj, void()) { os << obj; } template auto serialize_imp(std::ostream& os, T const& obj, long) -> decltype(obj.stream(os), void()) { obj.stream(os); } template auto serialize(std::ostream& os, T const& obj) -> decltype(serialize_imp(os, obj, 0), void()) { serialize_imp(os, obj, 0); } 

Ahora en algunas explicaciones. En primer lugar, uso la expresión SFINAE para excluir las funciones serialize(_imp) de la resolución de sobrecarga, si la primera expresión dentro de decltype no es válida (es decir, la función no existe).

El void() se utiliza para void() el tipo de devolución de todas esas funciones.

El argumento 0 se usa para preferir la sobrecarga os < < obj si ambos están disponibles (literal 0 es de tipo int y, como tal, la primera sobrecarga es una mejor coincidencia).


Ahora, es probable que desee un rasgo para comprobar si existe una función. Afortunadamente, es fácil escribir eso. Sin embargo, tenga en cuenta que necesita escribir un rasgo para cada nombre de función diferente que desee.

 #include  template struct sfinae_true : std::true_type{}; namespace detail{ template static auto test_stream(int) -> sfinae_true().stream(std::declval()))>; template static auto test_stream(long) -> std::false_type; } // detail:: template struct has_stream : decltype(detail::test_stream(0)){}; 

Ejemplo en vivo

Y a las explicaciones. Primero, sfinae_true es un tipo de ayuda, y básicamente equivale a escribir decltype(void(std::declval().stream(a0)), std::true_type{}) . La ventaja es simplemente que es más corto.
A continuación, la struct has_stream : decltype(...) hereda de std::true_type o std::false_type al final, dependiendo de si la comprobación de test_stream en test_stream falla o no.
Por último, std::declval le da un "valor" del tipo que pase, sin que necesite saber cómo puede construirlo. Tenga en cuenta que esto solo es posible dentro de un contexto no evaluado, como decltype , sizeof y otros.


Tenga en cuenta que decltype no es necesariamente necesario, ya que sizeof (y todos los contextos no evaluados) obtuvieron esa mejora. Es solo que decltype ya entrega un tipo y, como tal, es más limpio. Aquí hay una versión sizeof de una de las sobrecargas:

 template void serialize_imp(std::ostream& os, T const& obj, int, int(*)[sizeof((os < < obj),0)] = 0) { os << obj; } 

Los parámetros int y long están todavía ahí por la misma razón. El puntero de matriz se usa para proporcionar un contexto donde se puede usar sizeof .

C ++ permite usar SFINAE para esto (observe que con las características de C ++ 11 esto es más simple porque admite SFINAE extendido en expresiones casi arbitrarias; el siguiente fue diseñado para trabajar con comstackdores comunes de C ++ 03):

 #define HAS_MEM_FUNC(func, name) \ template \ struct name { \ typedef char yes[1]; \ typedef char no [2]; \ template  struct type_check; \ template  static yes &chk(type_check *); \ template  static no &chk(...); \ static bool const value = sizeof(chk(0)) == sizeof(yes); \ } 

la plantilla y la macro anteriores intentan crear una instancia de una plantilla, dándole un tipo de puntero de función miembro y el puntero de función miembro real. Si los tipos no se ajustan, SFINAE hace que la plantilla sea ignorada. Uso como este:

 HAS_MEM_FUNC(toString, has_to_string); template void doSomething() { if(has_to_string::value) { ... } else { ... } } 

Pero tenga en cuenta que no puede simplemente llamar a la función toString en esa twig si. dado que el comstackdor verificará la validez en ambas twigs, eso fallaría en los casos en que la función no exista. Una forma es usar SFINAE una vez más (enable_if se puede obtener también de boost):

 template struct enable_if { typedef T type; }; template struct enable_if { }; HAS_MEM_FUNC(toString, has_to_string); template typename enable_if::value, std::string>::type doSomething(T * t) { /* something when T has toString ... */ return t->toString(); } template typename enable_if< !has_to_string::value, std::string>::type doSomething(T * t) { /* something when T doesnt have toString ... */ return "T::toString() does not exist."; } 

Diviértete usándolo. La ventaja de esto es que también funciona para las funciones de miembros sobrecargadas, y también para las funciones miembro de const (recuerde utilizar std::string(T::*)() const como el tipo de puntero de función miembro a continuación!).

Aunque esta pregunta tiene dos años, me atrevo a agregar mi respuesta. Esperemos que aclare la solución anterior, indiscutiblemente excelente. Tomé las respuestas muy útiles de Nicola Bonelli y Johannes Schaub y las fusioné en una solución que es, en mi humilde opinión, más legible, clara y no requiere el tipo de extensión:

 template  class TypeHasToString { // This type won't compile if the second template parameter isn't of type T, // so I can put a function pointer type in the first parameter and the function // itself in the second thus checking that the function has a specific signature. template  struct TypeCheck; typedef char Yes; typedef long No; // A helper struct to hold the declaration of the function pointer. // Change it if the function signature changes. template  struct ToString { typedef void (T::*fptr)(); }; template  static Yes HasToString(TypeCheck< typename ToString::fptr, &T::toString >*); template  static No HasToString(...); public: static bool const value = (sizeof(HasToString(0)) == sizeof(Yes)); }; 

Lo comprobé con gcc 4.1.2. El mérito recae principalmente en Nicola Bonelli y Johannes Schaub, así que denles un voto si mi respuesta lo ayuda 🙂

Kit de herramientas de detección

N4502 propone una prueba de detección para su inclusión en la biblioteca estándar C ++ 17 que puede resolver el problema de una manera un tanto elegante. Además, acaba de ser aceptado en los fundamentos de la biblioteca TS v2. Introduce algunas metafunciones, incluida std::is_detected que se puede usar para escribir fácilmente metafunciones de detección de tipo o función en la parte superior. Aquí es cómo puedes usarlo:

 template using toString_t = decltype( std::declval().toString() ); template constexpr bool has_toString = std::is_detected_v; 

Tenga en cuenta que el ejemplo anterior no está probado. El kit de herramientas de detección aún no está disponible en las bibliotecas estándar, pero la propuesta contiene una implementación completa que puede copiar fácilmente si realmente la necesita. Funciona bien con la función C ++ 17 if constexpr :

 template std::string optionalToString(T* obj) { if constexpr (has_toString) return obj->toString(); else return "toString not defined"; } 

Boost.TTI

Otro juego de herramientas algo idiomático para realizar dicho control, aunque menos elegante, es Boost.TTI , introducido en Boost 1.54.0. Para su ejemplo, debería usar la macro BOOST_TTI_HAS_MEMBER_FUNCTION . Aquí es cómo puedes usarlo:

 #include  // Generate the metafunction BOOST_TTI_HAS_MEMBER_FUNCTION(toString) // Check whether T has a member function toString // which takes no parameter and returns a std::string constexpr bool foo = has_member_function_toString::value; 

Entonces, podrías usar el bool para crear un cheque SFINAE.

Explicación

La macro BOOST_TTI_HAS_MEMBER_FUNCTION genera la metafunción has_member_function_toString que toma el tipo verificado como su primer parámetro de plantilla. El segundo parámetro de plantilla corresponde al tipo de retorno de la función miembro, y los siguientes parámetros corresponden a los tipos de los parámetros de la función. El value miembro contiene true si la clase T tiene una función miembro std::string toString() .

Alternativamente, has_member_function_toString puede tomar un puntero de función miembro como un parámetro de plantilla. Por lo tanto, es posible reemplazar has_member_function_toString::value por has_member_function_toString::value .

Esto es para lo que están los rasgos de tipo. Lamentablemente, deben definirse manualmente. En tu caso, imagina lo siguiente:

 template  struct response_trait { static bool const has_tostring = false; }; template <> struct response_trait { static bool const has_tostring = true; } 

Una solución simple para C ++ 11:

 template auto optionalToString(T* obj) -> decltype( obj->toString() ) { return obj->toString(); } auto optionalToString(...) -> string { return "toString not defined"; } 

Actualización, 3 años después: (y esto no se ha probado). Para probar la existencia, creo que esto funcionará:

 template constexpr auto test_has_toString_method(T* obj) -> decltype( obj->toString() , std::true_type{} ) { return obj->toString(); } constexpr auto test_has_toString_method(...) -> std::false_type { return "toString not defined"; } 

Bueno, esta pregunta ya tiene una larga lista de respuestas, pero me gustaría enfatizar el comentario de Morwenn: hay una propuesta para C ++ 17 que lo hace mucho más simple. Consulte N4502 para obtener detalles, pero como ejemplo independiente, considere lo siguiente.

Esta parte es la parte constante, ponla en un encabezado.

 // See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4502.pdf. template  using void_t = void; // Primary template handles all types not supporting the operation. template  class, typename = void_t<>> struct detect : std::false_type {}; // Specialization recognizes/validates only types supporting the archetype. template  class Op> struct detect>> : std::true_type {}; 

luego está la parte variable, donde se especifica lo que se busca (un tipo, un tipo de miembro, una función, una función miembro, etc.). En el caso del OP:

 template  using toString_t = decltype(std::declval().toString()); template  using has_toString = detect; 

El siguiente ejemplo, tomado de N4502 , muestra una sonda más elaborada:

 // Archetypal expression for assignment operation. template  using assign_t = decltype(std::declval() = std::declval()) // Trait corresponding to that archetype. template  using is_assignable = detect; 

En comparación con las otras implementaciones descritas anteriormente, esta es bastante simple: un conjunto reducido de herramientas ( void_t y detect ) es suficiente, no hay necesidad de macros peludas. Además, se informó (ver N4502 ) que es mensurablemente más eficiente (tiempo de comstackción y consumo de memoria del comstackdor) que los enfoques anteriores.

Aquí hay un ejemplo en vivo . Funciona bien con Clang, pero desafortunadamente, las versiones de GCC anteriores al 5.1 seguían una interpretación diferente del estándar C ++ 11 que causaba que void_t no funcionara como se esperaba. Yakk ya proporcionó la solución alternativa: use la siguiente definición de void_t ( void_t en la lista de parámetros funciona pero no como tipo de devolución ):

 #if __GNUC__ < 5 && ! defined __clang__ // https://stackoverflow.com/a/28967049/1353549 template  struct voider { using type = void; }; template  using void_t = typename voider::type; #else template  using void_t = void; #endif 

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) 

Esta es una solución de C ++ 11 para el problema general si “Si hiciera X, ¿se comstackría?”

 template struct type_sink { typedef void type; }; // consumes a type, and makes it `void` template using type_sink_t = typename type_sink::type; template struct has_to_string : std::false_type {}; \ template struct has_to_string< T, type_sink_t< decltype( std::declval().toString() ) > >: std::true_type {}; 

Trait has_to_string tal que has_to_string::value es true si y solo si T tiene un método .toString que se puede invocar con 0 argumentos en este contexto.

A continuación, usaría despacho de tags:

 namespace details { template std::string optionalToString_helper(T* obj, std::true_type /*has_to_string*/) { return obj->toString(); } template std::string optionalToString_helper(T* obj, std::false_type /*has_to_string*/) { return "toString not defined"; } } template std::string optionalToString(T* obj) { return details::optionalToString_helper( obj, has_to_string{} ); } 

que tiende a ser más fácil de mantener que las expresiones SFINAE complejas.

Puedes escribir estos rasgos con una macro si te das cuenta de que lo haces mucho, pero son relativamente simples (unas pocas líneas cada uno) por lo que quizás no valga la pena:

 #define MAKE_CODE_TRAIT( TRAIT_NAME, ... ) \ template struct TRAIT_NAME : std::false_type {}; \ template struct TRAIT_NAME< T, type_sink_t< decltype( __VA_ARGS__ ) > >: std::true_type {}; 

lo que hace arriba es crear una macro MAKE_CODE_TRAIT . Le pasa el nombre del rasgo que desea, y algún código que puede probar el tipo T Así:

 MAKE_CODE_TRAIT( has_to_string, std::declval().toString() ) 

crea la clase de rasgos anterior.

Por otro lado, la técnica anterior es parte de lo que MS llama “expresión SFINAE”, y su comstackdor de 2013 falla bastante duro.

Tenga en cuenta que en C ++ 1y es posible la siguiente syntax:

 template std::string optionalToString(T* obj) { return compiled_if< has_to_string >(*obj, [&](auto&& obj) { return obj.toString(); }) *compiled_else ([&]{ return "toString not defined"; }); } 

que es una twig condicional de comstackción en línea que abusa de muchas características de C ++. Hacerlo probablemente no valga la pena, ya que el beneficio (del código que está en línea) no vale la pena el costo (de que nadie entienda cómo funciona), pero la existencia de la solución anterior puede ser de interés.

La solución estándar de C ++ presentada aquí por litb no funcionará como se espera si el método se define en una clase base.

Para una solución que maneje esta situación, consulte:

En ruso: http://www.rsdn.ru/forum/message/2759773.1.aspx

Traducción inglesa por Roman.Perepelitsa: http://groups.google.com/group/comp.lang.c++moderated/tree/browse_frm/thread/4f7c7a96f9afbe44/c95a7b4c645e449f?pli=1

Es increíblemente inteligente. Sin embargo, un problema con esta solución es que da compiler errors si el tipo que se prueba es uno que no se puede usar como una clase base (por ejemplo, tipos primitivos)

In Visual Studio, I noticed that if working with method having no arguments, an extra pair of redundant ( ) needs to be inserted around the argments to deduce( ) in the sizeof expression.

I wrote an answer to this in another thread that (unlike the solutions above) also checks inherited member functions:

SFINAE to check for inherited member functions

Here are some example from that solution:

Ejemplo 1:

We are checking for a member with the following signature: 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)); }; 

Please notice that it even checks the constness of the method, and works with primitive types, as well. (I mean has_const_begin::value is false and doesn’t cause a compile-time error.)

Ejemplo 2

Now we are looking for the signature: 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)); }; 

Please notice that MyClass doesn’t has to be default constructible or to satisfy any special concept. The technique works with template members, as well.

I am eagerly waiting opinions regarding this.

Now this was a nice little puzzle – great question!

Here’s an alternative to Nicola Bonelli’s solution that does not rely on the non-standard typeof operator.

Unfortunately, it does not work on GCC (MinGW) 3.4.5 or Digital Mars 8.42n, but it does work on all versions of MSVC (including VC6) and on Comeau C++.

The longer comment block has the details on how it works (or is supposed to work). As it says, I’m not sure which behavior is standards compliant – I’d welcome commentary on that.


update – 7 Nov 2008:

It looks like while this code is syntactically correct, the behavior that MSVC and Comeau C++ show does not follow the standard (thanks to Leon Timmermans and litb for pointing me in the right direction). The C++03 standard says the following:

14.6.2 Dependent names [temp.dep]

Paragraph 3

In the definition of a class template or a member of a class template, if a base class of the class template depends on a template-parameter, the base class scope is not examined during unqualified name lookup either at the point of definition of the class template or member or during an instantiation of the class template or member.

So, it looks like that when MSVC or Comeau consider the toString() member function of T performing name lookup at the call site in doToString() when the template is instantiated, that is incorrect (even though it’s actually the behavior I was looking for in this case).

The behavior of GCC and Digital Mars looks to be correct – in both cases the non-member toString() function is bound to the call.

Rats – I thought I might have found a clever solution, instead I uncovered a couple compiler bugs…


 #include  #include  struct Hello { std::string toString() { return "Hello"; } }; struct Generic {}; // the following namespace keeps the toString() method out of // most everything - except the other stuff in this // comstacktion unit namespace { std::string toString() { return "toString not defined"; } template  class optionalToStringImpl : public T { public: std::string doToString() { // in theory, the name lookup for this call to // toString() should find the toString() in // the base class T if one exists, but if one // doesn't exist in the base class, it'll // find the free toString() function in // the private namespace. // // This theory works for MSVC (all versions // from VC6 to VC9) and Comeau C++, but // does not work with MinGW 3.4.5 or // Digital Mars 8.42n // // I'm honestly not sure what the standard says // is the correct behavior here - it's sort // of like ADL (Argument Dependent Lookup - // also known as Koenig Lookup) but without // arguments (except the implied "this" pointer) return toString(); } }; } template  std::string optionalToString(T & obj) { // ugly, hacky cast... optionalToStringImpl* temp = reinterpret_cast*>( &obj); return temp->doToString(); } int main(int argc, char *argv[]) { Hello helloObj; Generic genericObj; std::cout < < optionalToString( helloObj) << std::endl; std::cout << optionalToString( genericObj) << std::endl; return 0; } 

MSVC has the __if_exists and __if_not_exists keywords ( Doc ). Together with the typeof-SFINAE approach of Nicola I could create a check for GCC and MSVC like the OP looked for.

Update: Source can be found Here

I modified the solution provided in https://stackoverflow.com/a/264088/2712152 to make it a bit more general. Also since it doesn’t use any of the new C++11 features we can use it with old compilers and should also work with msvc. But the compilers should enable C99 to use this since it uses variadic macros.

The following macro can be used to check if a particular class has a particular typedef or not.

 /** * @class : HAS_TYPEDEF * @brief : This macro will be used to check if a class has a particular * typedef or not. * @param typedef_name : Name of Typedef * @param name : Name of struct which is going to be run the test for * the given particular typedef specified in typedef_name */ #define HAS_TYPEDEF(typedef_name, name) \ template  \ struct name { \ typedef char yes[1]; \ typedef char no[2]; \ template  \ struct type_check; \ template  \ static yes& chk(type_check*); \ template  \ static no& chk(...); \ static bool const value = sizeof(chk(0)) == sizeof(yes); \ } 

The following macro can be used to check if a particular class has a particular member function or not with any given number of arguments.

 /** * @class : HAS_MEM_FUNC * @brief : This macro will be used to check if a class has a particular * member function implemented in the public section or not. * @param func : Name of Member Function * @param name : Name of struct which is going to be run the test for * the given particular member function name specified in func * @param return_type: Return type of the member function * @param ellipsis(...) : Since this is macro should provide test case for every * possible member function we use variadic macros to cover all possibilities */ #define HAS_MEM_FUNC(func, name, return_type, ...) \ template  \ struct name { \ typedef return_type (T::*Sign)(__VA_ARGS__); \ typedef char yes[1]; \ typedef char no[2]; \ template  \ struct type_check; \ template  \ static yes& chk(type_check*); \ template  \ static no& chk(...); \ static bool const value = sizeof(chk(0)) == sizeof(yes); \ } 

We can use the above 2 macros to perform the checks for has_typedef and has_mem_func as:

 class A { public: typedef int check; void check_function() {} }; class B { public: void hello(int a, double b) {} void hello() {} }; HAS_MEM_FUNC(check_function, has_check_function, void, void); HAS_MEM_FUNC(hello, hello_check, void, int, double); HAS_MEM_FUNC(hello, hello_void_check, void, void); HAS_TYPEDEF(check, has_typedef_check); int main() { std::cout < < "Check Function A:" << has_check_function::value < < std::endl; std::cout << "Check Function B:" << has_check_function::value < < std::endl; std::cout << "Hello Function A:" << hello_check::value < < std::endl; std::cout << "Hello Function B:" << hello_check::value < < std::endl; std::cout << "Hello void Function A:" << hello_void_check::value < < std::endl; std::cout << "Hello void Function B:" << hello_void_check::value < < std::endl; std::cout << "Check Typedef A:" << has_typedef_check::value < < std::endl; std::cout << "Check Typedef B:" << has_typedef_check::value < < std::endl; } 

Strange nobody suggested the following nice trick I saw once on this very site :

 template  struct has_foo { struct S { void foo(...); }; struct derived : S, T {}; template  struct W {}; template  char (&test(W *))[1]; template  char (&test(...))[2]; static const bool value = sizeof(test(0)) == 1; }; 

You have to make sure T is a class. It seems that ambiguity in the lookup of foo is a substitution failure. I made it work on gcc, not sure if it is standard though.

The generic template that can be used for checking if some “feature” is supported by the type:

 #include  template  

The template that checks whether there is a method foo that is compatible with signature double(const char*)

 // if T doesn't have foo method with the signature that allows to compile the bellow // expression then instantiating this template is Substitution Failure (SF) // which Is Not An Error (INAE) if this happens during overload resolution template  using has_foo = decltype(double(std::declval().foo(std::declval()))); 

Ejemplos

 // types that support has_foo struct struct1 { double foo(const char*); }; // exact signature match struct struct2 { int foo(const std::string &str); }; // compatible signature struct struct3 { float foo(...); }; // compatible ellipsis signature struct struct4 { template  int foo(T t); }; // compatible template signature // types that do not support has_foo struct struct5 { void foo(const char*); }; // returns void struct struct6 { std::string foo(const char*); }; // std::string can't be converted to double struct struct7 { double foo( int *); }; // const char* can't be converted to int* struct struct8 { double bar(const char*); }; // there is no foo method int main() { std::cout < < std::boolalpha; std::cout << is_supported::value < < std::endl; // false std::cout << is_supported::value < < std::endl; // false std::cout << is_supported::value < < std::endl; // true std::cout << is_supported::value < < std::endl; // true std::cout << is_supported::value < < std::endl; // true std::cout << is_supported::value < < std::endl; // true std::cout << is_supported::value < < std::endl; // false std::cout << is_supported::value < < std::endl; // false std::cout << is_supported::value < < std::endl; // false std::cout << is_supported::value < < std::endl; // false return 0; } 

http://coliru.stacked-crooked.com/a/83c6a631ed42cea4

There are a lot of answers here, but I failed, to find a version, that performs real method resolution ordering, while not using any of the newer c++ features (only using c++98 features).
Note: This version is tested and working with vc++2013, g++ 5.2.0 and the onlline compiler.

So I came up with a version, that only uses sizeof():

 template T declval(void); struct fake_void { }; template T &operator,(T &,fake_void); template T const &operator,(T const &,fake_void); template T volatile &operator,(T volatile &,fake_void); template T const volatile &operator,(T const volatile &,fake_void); struct yes { char v[1]; }; struct no { char v[2]; }; template struct yes_no:yes{}; template<> struct yes_no:no{}; template struct has_awesome_member { template static yes_no< (sizeof(( declval().awesome_member(),fake_void() ))!=0)> check(int); template static no check(...); enum{value=sizeof(check(0)) == sizeof(yes)}; }; struct foo { int awesome_member(void); }; struct bar { }; struct foo_void { void awesome_member(void); }; struct wrong_params { void awesome_member(int); }; static_assert(has_awesome_member::value,""); static_assert(!has_awesome_member::value,""); static_assert(has_awesome_member::value,""); static_assert(!has_awesome_member::value,""); 

Live demo (with extended return type checking and vc++2010 workaround): http://cpp.sh/5b2vs

No source, as I came up with it myself.

When running the Live demo on the g++ compiler, please note that array sizes of 0 are allowed, meaning that the static_assert used will not trigger a compiler error, even when it fails.
A commonly used work-around is to replace the ‘typedef’ in the macro with ‘extern’.

An example using SFINAE and template partial specialization, by writing a Has_foo concept check:

 #include  struct A{}; struct B{ int foo(int a, int b);}; struct C{void foo(int a, int b);}; struct D{int foo();}; struct E: public B{}; // available in C++17 onwards as part of  template using void_t = void; template struct Has_foo: std::false_type{}; template struct Has_foo().foo((int)0, (int)0)) >::value > >>: std::true_type{}; static_assert(not Has_foo::value, "A does not have a foo"); static_assert(Has_foo::value, "B has a foo"); static_assert(not Has_foo::value, "C has a foo with the wrong return. "); static_assert(not Has_foo::value, "D has a foo with the wrong arguments. "); static_assert(Has_foo::value, "E has a foo since it inherits from B"); 

How about this solution?

 #include  template  struct hasToString : std::false_type { }; template  struct hasToString::type > : std::true_type { }; 

Here is my version that handles all possible member function overloads with arbitrary arity, including template member functions, possibly with default arguments. It distinguishes 3 mutually exclusive scenarios when making a member function call to some class type, with given arg types: (1) valid, or (2) ambiguous, or (3) non-viable. Ejemplo de uso:

 #include  #include  HAS_MEM(bar) HAS_MEM_FUN_CALL(bar) struct test { void bar(int); void bar(double); void bar(int,double); template < typename T > typename std::enable_if< not std::is_integral::value >::type bar(const T&, int=0){} template < typename T > typename std::enable_if< std::is_integral::value >::type bar(const std::vector&, T*){} template < typename T > int bar(const std::string&, int){} }; 

Now you can use it like this:

 int main(int argc, const char * argv[]) { static_assert( has_mem_bar::value , ""); static_assert( has_valid_mem_fun_call_bar::value , ""); static_assert( has_valid_mem_fun_call_bar::value , ""); static_assert( has_valid_mem_fun_call_bar, int*)>::value , ""); static_assert( has_no_viable_mem_fun_call_bar, double*)>::value , ""); static_assert( has_valid_mem_fun_call_bar::value , ""); static_assert( std::is_same::type>::value , ""); static_assert( has_valid_mem_fun_call_bar::value , ""); static_assert( not has_valid_mem_fun_call_bar::value , ""); static_assert( not has_ambiguous_mem_fun_call_bar::value , ""); static_assert( has_ambiguous_mem_fun_call_bar::value , ""); static_assert( has_viable_mem_fun_call_bar::value , ""); static_assert( has_viable_mem_fun_call_bar::value , ""); static_assert( has_no_viable_mem_fun_call_bar::value , ""); return 0; } 

Here is the code, written in c++11, however, you can easily port it (with minor tweaks) to non-c++11 that has typeof extensions (eg gcc). You can replace the HAS_MEM macro with your own.

 #pragma once #if __cplusplus >= 201103 #include  #include  #define HAS_MEM(mem) \ \ template < typename T > \ struct has_mem_##mem \ { \ struct yes {}; \ struct no {}; \ \ struct ambiguate_seed { char mem; }; \ template < typename U > struct ambiguate : U, ambiguate_seed {}; \ \ template < typename U, typename = decltype(&U::mem) > static constexpr no test(int); \ template < typename > static constexpr yes test(...); \ \ static bool constexpr value = std::is_same >(0)),yes>::value ; \ typedef std::integral_constant type; \ }; #define HAS_MEM_FUN_CALL(memfun) \ \ template < typename Signature > \ struct has_valid_mem_fun_call_##memfun; \ \ template < typename T, typename... Args > \ struct has_valid_mem_fun_call_##memfun< T(Args...) > \ { \ struct yes {}; \ struct no {}; \ \ template < typename U, bool = has_mem_##memfun::value > \ struct impl \ { \ template < typename V, typename = decltype(std::declval().memfun(std::declval()...)) > \ struct test_result { using type = yes; }; \ \ template < typename V > static constexpr typename test_result::type test(int); \ template < typename > static constexpr no test(...); \ \ static constexpr bool value = std::is_same(0)),yes>::value; \ using type = std::integral_constant; \ }; \ \ template < typename U > \ struct impl : std::false_type {}; \ \ static constexpr bool value = impl::value; \ using type = std::integral_constant; \ }; \ \ template < typename Signature > \ struct has_ambiguous_mem_fun_call_##memfun; \ \ template < typename T, typename... Args > \ struct has_ambiguous_mem_fun_call_##memfun< T(Args...) > \ { \ struct ambiguate_seed { void memfun(...); }; \ \ template < class U, bool = has_mem_##memfun::value > \ struct ambiguate : U, ambiguate_seed \ { \ using ambiguate_seed::memfun; \ using U::memfun; \ }; \ \ template < class U > \ struct ambiguate : ambiguate_seed {}; \ \ static constexpr bool value = not has_valid_mem_fun_call_##memfun< ambiguate(Args...) >::value; \ using type = std::integral_constant; \ }; \ \ template < typename Signature > \ struct has_viable_mem_fun_call_##memfun; \ \ template < typename T, typename... Args > \ struct has_viable_mem_fun_call_##memfun< T(Args...) > \ { \ static constexpr bool value = has_valid_mem_fun_call_##memfun::value \ or has_ambiguous_mem_fun_call_##memfun::value; \ using type = std::integral_constant; \ }; \ \ template < typename Signature > \ struct has_no_viable_mem_fun_call_##memfun; \ \ template < typename T, typename... Args > \ struct has_no_viable_mem_fun_call_##memfun < T(Args...) > \ { \ static constexpr bool value = not has_viable_mem_fun_call_##memfun::value; \ using type = std::integral_constant; \ }; \ \ template < typename Signature > \ struct result_of_mem_fun_call_##memfun; \ \ template < typename T, typename... Args > \ struct result_of_mem_fun_call_##memfun< T(Args...) > \ { \ using type = decltype(std::declval().memfun(std::declval()...)); \ }; #endif 

You can skip all the metaprogramming in C++14, and just write this using fit::conditional from the Fit library:

 template std::string optionalToString(T* x) { return fit::conditional( [](auto* obj) -> decltype(obj->toString()) { return obj->toString(); }, [](auto*) { return "toString not defined"; } )(x); } 

You can also create the function directly from the lambdas as well:

 FIT_STATIC_LAMBDA_FUNCTION(optionalToString) = fit::conditional( [](auto* obj) -> decltype(obj->toString(), std::string()) { return obj->toString(); }, [](auto*) -> std::string { return "toString not defined"; } ); 

However, if you are using a compiler that doesn’t support generic lambdas, you will have to write separate function objects:

 struct withToString { template auto operator()(T* obj) const -> decltype(obj->toString(), std::string()) { return obj->toString(); } }; struct withoutToString { template std::string operator()(T*) const { return "toString not defined"; } }; FIT_STATIC_FUNCTION(optionalToString) = fit::conditional( withToString(), withoutToString() ); 

Here is an example of the working code.

 template using toStringFn = decltype(std::declval().toString()); template * = nullptr> std::string optionalToString(const T* obj, int) { return obj->toString(); } template  std::string optionalToString(const T* obj, long) { return "toString not defined"; } int main() { A* a; B* b; std::cout < < optionalToString(a, 0) << std::endl; // This is A std::cout << optionalToString(b, 0) << std::endl; // toString not defined } 

toStringFn* = nullptr will enable the function which takes extra int argument which has a priority over function which takes long when called with 0 .

You can use the same principle for the functions which returns true if function is implemented.

 template  constexpr bool toStringExists(long) { return false; } template * = nullptr> constexpr bool toStringExists(int) { return true; } int main() { A* a; B* b; std::cout < < toStringExists(0) < < std::endl; // true std::cout << toStringExists(0) < < std::endl; // false }