#include struct A {}; std::string to_string(const A&) { return "I am a A!"; } // Type B with a serialize method. struct B { std::string serialize() const { return "I am a B!"; } }; // Type C with a "wrong" serialize member (not a method) and a to_string overload. struct C { std::string serialize; }; std::string to_string(const C&) { return "I am a C!"; } struct D : A { std::string serialize() const { return "I am a D!"; } }; struct E { struct Functor { std::string operator()(){ return "I am a E!"; } }; Functor serialize; }; // Structures are good as they expose everything in public! template struct hasSerialize { // For the compile time comparison. typedef char yes[1]; typedef yes no[2]; // This helper struct permits us to check two properties of a template argument. template struct reallyHas; // Two overloads for yes: one if for the signature of a normal method, one is for the signature of a const method. template static yes& test(reallyHas* /*unused*/) { } template static yes& test(reallyHas* /*unused*/) { } // The famous C++ sink-hole. // Note that sink-hole must be templated too as we are testing test(0). template static no& test(...) { /* dark matter */ } // The constant used as a return value for the test. enum { value = sizeof(test(0)) == sizeof(yes) }; }; template bool testHasSerialize(const T& /*t*/) { return hasSerialize::value; } template struct enable_if {}; template struct enable_if { typedef T type; }; template typename enable_if::value, std::string>::type serialize(const T& obj) { return obj.serialize(); } template typename enable_if::value, std::string>::type serialize(const T& obj) { return to_string(obj); } int main() { A a; B b; C c; std::cout << serialize(a) << std::endl; std::cout << serialize(b) << std::endl; std::cout << serialize(c) << std::endl; return 0; }