Using SFINAE, i can detect wether a given class has a certain member function. But what if i want to test for inherited member functions?
The following does not work in VC8 and GCC4 (i.e. detects that A
has a member function foo()
, but not that B
inherits one):
#include <iostream> template<typename T, typename Sig> struct has_foo { template <typename U, U> struct type_check; template <typename V> static char (& chk(type_check<Sig, &V::foo>*))[1]; template <typename > static char (& chk(...))[2]; static bool const value = (sizeof(chk<T>(0)) == 1); }; struct A { void foo(); }; struct B : A {}; int main() { using namespace std; cout << boolalpha << has_foo<A, void (A::*)()>::value << endl; // true cout << boolalpha << has_foo<B, void (B::*)()>::value << endl; // false }
So, is there a way to test for inherited member functions?
Take a look at this thread:
http://lists.boost.org/boost-users/2009/01/44538.php
Derived from the code linked to in that discussion:
#include <iostream> template <typename Type> class has_foo { class yes { char m;}; class no { yes m[2];}; struct BaseMixin { void foo(){} }; struct Base : public Type, public BaseMixin {}; template <typename T, T t> class Helper{}; template <typename U> static no deduce(U*, Helper<void (BaseMixin::*)(), &U::foo>* = 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<A>::result << endl; cout << boolalpha << has_foo<B>::result << endl; cout << boolalpha << has_foo<C>::result; }
Result:
true true false
joshperry's answer is very clever and elegant, but (as it is stated below the post) it doesn't check the signature of foo() properly and doesn't work with fundamental types (like int): it causes a compiler error. I will propose a technique that handles inherited members correctly and also checks the signature of the member function. Instead of going into details I will give you two exampes and hope that the code will speak for itself.
We are checking for a member with the following signature: T::const_iterator begin() const
template<class T> struct has_const_begin { typedef char (&Yes)[1]; typedef char (&No)[2]; template<class U> static Yes test(U const * data, typename std::enable_if<std::is_same< typename U::const_iterator, decltype(data->begin()) >::value>::type * = 0); static No test(...); static const bool value = sizeof(Yes) == sizeof(has_const_begin::test((typename std::remove_reference<T>::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<int>::value
is false and doesn't cause a compile-time error.)
Now we are looking for the signature: void foo(MyClass&, unsigned)
template<class T> struct has_foo { typedef char (&Yes)[1]; typedef char (&No)[2]; template<class U> static Yes test(U * data, MyClass* arg1 = 0, typename std::enable_if<std::is_void< decltype(data->foo(*arg1, 1u)) >::value>::type * = 0); static No test(...); static const bool value = sizeof(Yes) == sizeof(has_foo::test((typename std::remove_reference<T>::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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With