Ist es möglich, eine Vorlage zu schreiben, um nach der Existenz einer function zu suchen?

Ist es möglich, eine Vorlage zu schreiben, die das Verhalten ändert, abhängig davon, ob eine bestimmte Mitgliedsfunktion in einer class definiert ist?

Hier ist ein einfaches Beispiel dafür, was ich schreiben möchte:

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

Wenn die class T also toString() definiert hat, wird sie verwendet. Sonst nicht. Der magische Teil, den ich nicht kenne, ist der “FUNCTION_EXISTS” -Teil.

Solutions Collecting From Web of "Ist es möglich, eine Vorlage zu schreiben, um nach der Existenz einer function zu suchen?"

Ja, mit SFINAE können Sie prüfen, ob eine bestimmte class eine bestimmte Methode anbietet. Hier ist der Arbeitscode:

 #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; } 

Ich habe es gerade mit Linux und gcc 4.1 / 4.3 getestet. Ich weiß nicht, ob es auf anderen Plattformen mit anderen Compilern portierbar ist.

Diese Frage ist alt, aber mit C ++ 11 haben wir einen neuen Weg gefunden, um nach einer Existenz von functionen (oder der Existenz eines beliebigen Nicht-Typ-Members) zu suchen, wobei wir uns wieder auf SFINAE verlassen:

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

Jetzt zu einigen Erklärungen. Als erstes benutze ich den Ausdruck SFINAE um die serialize(_imp) functionen von der Überladungsauflösung auszuschließen, wenn der erste Ausdruck innerhalb von decltype nicht gültig ist (aka, die function existiert nicht).

Der void() wird verwendet, um den Rückgabetyp all dieser functionen void .

Das Argument 0 wird verwendet, um die Überladung von os < < obj zu bevorzugen, wenn beide verfügbar sind (Literal 0 ist vom Typ int und daher ist die erste Überladung eine bessere Übereinstimmung).


Wahrscheinlich möchten Sie nun, dass eine function überprüft, ob eine function vorhanden ist. Zum Glück ist es einfach, das zu schreiben. Beachten Sie jedoch, dass Sie für jeden gewünschten functionsnamen selbst ein Merkmal schreiben müssen.

 #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)){}; 

Live-Beispiel

Und weiter zu Erklärungen. Erstens ist sfinae_true ein sfinae_true und entspricht im Prinzip dem Schreiben von decltype(void(std::declval().stream(a0)), std::true_type{}) . Der Vorteil ist einfach, dass es kürzer ist.
Als nächstes erbt die struct has_stream : decltype(...) am Ende entweder von std::true_type oder std::false_type , abhängig davon, ob das decltype check in test_stream fehlschlägt oder nicht.
Als letztes gibt std::declval einen "Wert" von dem Typ, den Sie übergeben, ohne dass Sie wissen müssen, wie Sie ihn konstruieren können. Beachten Sie, dass dies nur in einem nicht evaluierten Kontext möglich ist, z. B. decltype , sizeof und andere.


Beachten Sie, dass decltype nicht unbedingt benötigt wird, da sizeof (und alle nicht bewerteten Kontexte) diese Erweiterung erhalten haben. Es ist nur, dass decltype bereits einen Typ liefert und als solcher einfach sauberer ist. Hier ist eine sizeof Version einer der Überladungen:

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

Die Parameter int und long sind aus demselben Grund immer noch da. Der Array-pointers wird verwendet, um einen Kontext bereitzustellen, in dem sizeof verwendet werden kann.

C ++ ermöglicht die Verwendung von SFINAE (beachten Sie, dass dies bei C ++ 11-functionen einfacher ist, da es erweiterte SFINAE für nahezu beliebige Ausdrücke unterstützt – das Folgende wurde so entworfen, dass es mit gängigen C ++ 03-Compilern funktioniert):

 #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); \ } 

Die obige Vorlage und das obige Makro versuchen, eine Vorlage zu instanziieren, indem sie ihr einen pointers auf den Typ der Mitgliedsfunktion und den eigentlichen pointers auf die Mitgliedsfunktion geben. Wenn die Typen nicht passen, bewirkt SFINAE, dass die Vorlage ignoriert wird. Verwendung wie folgt:

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

Beachten Sie jedoch, dass Sie diese toString function nicht nur in der if-Verzweigung aufrufen können. da der Compiler in beiden Zweigen auf Gültigkeit prüft, würde das in Fällen fehlschlagen, in denen die function nicht existiert. Eine Möglichkeit ist, SFINAE noch einmal zu benutzen (enable_if kann auch von Boost bezogen werden):

 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."; } 

Viel Spaß beim Benutzen. Der Vorteil davon ist, dass es auch für überladene Elementfunktionen und auch für konstante Elementfunktionen funktioniert (denken Sie daran, dass Sie std::string(T::*)() const als std::string(T::*)() const pointerstyp verwenden!).

Obwohl diese Frage zwei Jahre alt ist, wage ich es, meine Antwort hinzuzufügen. Hoffentlich wird es die vorherige, unbestreitbar ausgezeichnete Lösung klären. Ich habe die sehr hilfreiche Antwort von Nicola Bonelli und Johannes Schaub genommen und sie zu einer Lösung zusammengefügt, die, IMHO, lesbarer und übersichtlicher ist und nicht den typeof Erweiterung benötigt:

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

Ich habe es mit gcc 4.1.2 überprüft. Der Dank geht hauptsächlich an Nicola Bonelli und Johannes Schaub, also gib ihnen eine Stimme, wenn meine Antwort dir hilft 🙂

Erkennungs-Toolkit

N4502 schlägt einen Nachweis vor, der für die Aufnahme in die C ++ 17-Standardbibliothek gedacht ist, die das Problem in einer etwas eleganten Weise lösen kann. Außerdem wurde es gerade in die Bibliotheksgrundlagen TS v2 aufgenommen. Es führt einige Metafunktionen ein, einschließlich std::is_detected die verwendet werden können, um einfach Metafunktionen für Typ- oder functionserkennung obenauf zu schreiben. Hier ist, wie Sie es verwenden könnten:

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

Beachten Sie, dass das obige Beispiel nicht getestet wurde. Das Erkennungs-Toolkit ist noch nicht in Standardbibliotheken verfügbar, aber das Angebot enthält eine vollständige Implementierung, die Sie einfach kopieren können, wenn Sie sie wirklich benötigen. Es spielt gut mit dem C ++ 17 Feature, if constexpr :

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

Boost.TTI

Ein weiteres etwas idiomatisches Toolkit, um einen solchen Check durchzuführen – wenn auch weniger elegant – ist Boost.TTI , eingeführt in Boost 1.54.0. In Ihrem Beispiel müssten Sie das Makro BOOST_TTI_HAS_MEMBER_FUNCTION . Hier ist, wie Sie es verwenden könnten:

 #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; 

Dann könnten Sie den bool , um einen SFINAE-Check zu erstellen.

Erläuterung

Das Makro BOOST_TTI_HAS_MEMBER_FUNCTION generiert die Metafunktion has_member_function_toString die den geprüften Typ als ersten Template-Parameter verwendet. Der zweite Vorlagenparameter entspricht dem Rückgabetyp der Elementfunktion, und die folgenden Parameter entsprechen den Typen der functionsparameter. Der Member- value enthält true wenn die class T eine std::string toString() .

Alternativ kann has_member_function_toString einen has_member_function_toString als has_member_function_toString verwenden. Daher ist es möglich, has_member_function_toString::value durch has_member_function_toString::value zu ersetzen.

Dafür gibt es die Charaktereigenschaften. Leider müssen sie manuell definiert werden. Stellen Sie sich in Ihrem Fall Folgendes vor:

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

Eine einfache Lösung für C ++ 11:

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

Update, 3 Jahre später: (und das ist ungeprüft). Um auf die Existenz zu testen, denke ich, dass das funktionieren wird:

 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"; } 

Nun, diese Frage hat schon eine lange Liste von Antworten, aber ich möchte den Kommentar von Morwenn hervorheben: Es gibt einen Vorschlag für C ++ 17, der es wirklich viel einfacher macht. Weitere Informationen finden Sie in N4502 , aber betrachten Sie als eigenständiges Beispiel Folgendes.

Dieser Teil ist der konstante Teil, setzen Sie es in einen Header.

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

Dann gibt es den variablen Teil, in dem Sie angeben, wonach Sie suchen (ein Typ, ein Elementtyp, eine function, eine Elementfunktion usw.). Im Falle des OP:

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

Das folgende Beispiel aus N4502 zeigt eine ausführlichere Sonde:

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

Im Vergleich zu den anderen oben beschriebenen Implementierungen ist dies ziemlich einfach: ein reduzierter Satz von Werkzeugen ( void_t und detect ) reicht aus, keine Notwendigkeit für behaarte Makros. Außerdem wurde berichtet (siehe N4502 ), dass es messbar effizienter ist (Kompilierungszeit und Compilerspeicherverbrauch) als frühere Ansätze.

Hier ist ein Live-Beispiel . Es funktioniert gut mit Clang, aber leider GCC-Versionen vor 5.1 folgte eine andere Interpretation des C ++ 11-Standards, die void_t nicht wie erwartet funktioniert. Yakk hat das Workaround bereits bereitgestellt: Verwenden Sie die folgende Definition von void_t ( void_t in der Parameterliste funktioniert, aber nicht als Rückgabetyp ):

 #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 

Hier sind einige Verwendungsschnipsel: * Die Eingeweide für all das sind weiter unten

Suchen Sie nach Mitglied x in einer bestimmten class. Könnte var, func, class, union oder enum sein:

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

Suchen Sie nach Mitgliedsfunktion 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; 

Auf Membervariable x :

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

Suchen Sie nach Mitgliedsklasse x :

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

Suche nach Mitgliedsverband x :

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

Suche nach Member-Enum x :

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

Suchen Sie nach einer Mitgliedsfunktion x unabhängig von der Signatur:

 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; 

ODER

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

Details und core:

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

Makros (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) 

Dies ist eine C ++ 11-Lösung für das allgemeine Problem, wenn “Wenn ich X gemacht hätte, würde es kompilieren?”

 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 so dass has_to_string::value true ist true wenn und nur wenn T eine Methode .toString , die in diesem Kontext mit 0 Argumenten aufgerufen werden kann.

Als Nächstes würde ich die Tag-Verteilung verwenden:

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

welches tendenziell wartungsfreundlicher ist als komplexe SFINAE-Ausdrücke.

Sie können diese Merkmale mit einem Makro schreiben, wenn Sie feststellen, dass Sie viel tun, aber sie sind relativ einfach (jeweils ein paar Zeilen), also vielleicht nicht wert:

 #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 {}; 

Was das obige bewirkt, ist ein Makro MAKE_CODE_TRAIT erstellen. Sie übergeben ihm den Namen des gewünschten Merkmals und einen Code, der den Typ T testen kann. So:

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

erstellt die oben genannte Merkmalklasse.

Nebenbei bemerkt ist die obige Technik Teil dessen, was MS “Ausdruck SFINAE” nennt, und ihr 2013er Compiler versagt ziemlich hart.

Beachten Sie, dass in C ++ 1y die folgende Syntax möglich ist:

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

Das ist eine bedingte Inline-Verzweigung, die viele C ++ – functionen ausnutzt. Dies zu tun, ist es wahrscheinlich nicht wert, da der Nutzen (des Codes, der inline ist) die Kosten nicht wert ist (da niemand versteht, wie es funktioniert), aber die Existenz dieser obigen Lösung könnte von Interesse sein.

Die hier vorgestellte Standard-C ++ – Lösung von litb funktioniert nicht wie erwartet, wenn die Methode zufällig in einer Basisklasse definiert ist.

Eine Lösung, die diese Situation behandelt, finden Sie unter:

In Russisch: http://www.rsdn.ru/forum/message/2759773.1.aspx

English Translation by Roman.Perepelitsa : http://groups.google.com/group/comp.lang.c++.moderated/tree/browse_frm/thread/4f7c7a96f9afbe44/c95a7b4c645e449f?pli=1

It is insanely clever. However one issue with this solutiion is that gives compiler errors if the type being tested is one that cannot be used as a base class (eg primitive types)

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:

Example1:

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.)

Example 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 // compilation 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()))); 

Beispiele

 // 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. Beispielverwendung:

 #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 }