Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SFINAE to check for inherited member functions

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?

like image 922
Georg Fritzsche Avatar asked Dec 27 '09 16:12

Georg Fritzsche


2 Answers

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 
like image 71
joshperry Avatar answered Sep 19 '22 04:09

joshperry


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.

Example1:

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

Example 2

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.

like image 28
kispaljr Avatar answered Sep 21 '22 04:09

kispaljr