Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to check if a member function is defined for a class even if the member is inherited from an unknown base class

Tags:

c++

sfinae

I found similar questions and answers like this one. However, as I tried out, this SFINAE tests only succeeded if the tested member is directly defined in the class being tested. For example the following, class B, D1 print HAS while the other two print NOT HAS. Is there a way to determine that if a class has a member, whether it is defined by itself, or a base class, and the name of the base class is not known in this case. The motivation is that I want to write a generic function that will call a certain method if it exists (from base or not, the type of the parameter is generic, leave along the type of its possible base).

#include <iostream>

class HasFoo
{
    public :

    typedef char Small;
    typedef struct {char; char;} Large;

    template <typename C, void (C::*) ()> class SFINAE {};

    template <typename C> static Small test (SFINAE<C, &C::foo> *)
    {
        std::cout << "HAS" << std::endl;
    }

    template <typename C> static Large test (...)
    {
        std::cout << "NOT HAS" << std::endl;
    }
};

class B
{
    public :

    void foo () {}
};

class D1 : public B
{
    public :

    void foo () {} // overide
};

class D2 : public B
{
    public :

    using B::foo;
};

class D3 : public B {};

int main ()
{
    HasFoo::test<B>(0);
    HasFoo::test<D1>(0);
    HasFoo::test<D2>(0);
    HasFoo::test<D3>(0);
}
like image 585
Yan Zhou Avatar asked Jun 13 '12 04:06

Yan Zhou


2 Answers

In C++03, this is unfortunately not possible, sorry.

In C++11, things get much easier thanks to the magic of decltype. decltype lets you write expressions to deduce the type of their result, so you can perfectly name a member of a base class. And if the method is template, then SFINAE applies to the decltype expression.

#include <iostream>

template <typename T>
auto has_foo(T& t) -> decltype(t.foo(), bool()) { return true; }

bool has_foo(...) { return false; }


struct Base {
    void foo() {}
};

struct Derived1: Base {
    void foo() {}
};

struct Derived2: Base {
    using Base::foo;
};

struct Derived3: Base {
};

int main() {
    Base b; Derived1 d1; Derived2 d2; Derived3 d3;

    std::cout << has_foo(b) << " "
              << has_foo(d1) << " "
              << has_foo(d2) << " "
              << has_foo(d3) << "\n";
}

Unfortunately ideone has a version of gcc that's too old for this and clang 3.0 is no better.

like image 192
Matthieu M. Avatar answered Oct 11 '22 01:10

Matthieu M.


Unfortunately it wouldn't be possible at least in C++03 and I doubt in C++11 also.

Few important points:

  1. The proposed SFINAE works only if the method is public
  2. Even if the SFINAE would have worked for base methods, the point (1) applies; because for private and protected inheritance the SFINAE may end up useless
  3. Assuming you may want to deal only with public method/inheritance, the code HasFoo::test<> can be enhanced for taking multiple parameters where a base class also can be passed; std::is_base_of<> can be used for further validation of the base/derived relationship; then apply the same logic for base class also
like image 25
iammilind Avatar answered Oct 11 '22 02:10

iammilind