Check if member function exists and is not inherit

2019-04-13 06:27发布

How can I check if a member function exists and is not inherited?

I need this to resolve ambiguity for the following example:

A type either has a foo() or a bar() member function. Caller will call the one that exists for the given type. However, DerivedWithBar inherits foo() from BaseWithFoo but defines its own bar(). Thus, Caller does not know which function to call.

I'd need a way to give the non-inherited foo precedence over the inherited bar(), but I do not know how to check if a member function is inherited or not.

#include <iostream>

struct BaseWithFoo
{
    template <typename T> void foo(T&&){std::cout << "Base::foo" << std::endl;}
};

struct DerivedWithBar : public BaseWithFoo
{
    template <typename T> void bar(T&&){std::cout << "DerivedWithBar::bar" << std::endl;}
};

struct DerivedWithFoo : public BaseWithFoo
{
    template <typename T> void foo(T&&){std::cout << "DerivedWithFoo::foo" << std::endl;}
};

struct EmptyDerived : public BaseWithFoo {};

struct BaseWithBar
{
    template <typename T> void bar(T&&){std::cout << "BaseWithBar::bar" << std::endl;}
};


struct Caller
{
    template <typename T>
    auto call(T&& x) -> decltype(x.foo(*this), void())
    {
        x.foo(*this);
    }

    template <typename T>
    auto call(T&& x) -> decltype(x.bar(*this), void())
    {
        x.bar(*this);
    }
};

int main()
{
  Caller c;
  c.call(BaseWithFoo());
  c.call(DerivedWithFoo());
  c.call(DerivedWithBar());
  c.call(EmptyDerived());
  c.call(BaseWithBar());
}

live example

desired output:

Base::foo
DerivedWithFoo::foo
DerivedWithBar::bar
Base::foo
BaseWithBar::bar

2条回答
走好不送
2楼-- · 2019-04-13 06:56

I found a way to distinguish between inherited and non-inherited member functions by comparing types of member function pointers.

The following is a partial solution to my full problem ("giving non-inherited member functions precedence over inherited ones"). This will only call non-inherited foo or non-inherited bar.

struct Caller
{    
    template <typename T>
    auto call(T&& x) -> decltype(x.foo(*this),
        std::enable_if_t<
            std::is_same<
                decltype(&T::template foo<decltype(*this)>),
                void (T::*)(decltype(*this))
            >::value
        >())
    {
        x.foo(*this);
    }

    template <typename T>
    auto call(T&& x) -> decltype(x.bar(*this),
        std::enable_if_t<
            std::is_same<
                decltype(&T::template bar<decltype(*this)>),
                void (T::*)(decltype(*this))
            >::value
        >())
    {
        x.bar(*this);
    }
};

live example

查看更多
姐就是有狂的资本
3楼-- · 2019-04-13 07:07

With a bunch of template, we can get it as it follows:

#include<iostream>
#include<type_traits>
#include<utility>

struct BaseWithFoo
{
    template <typename T> void foo(T&&){std::cout << "Base::foo" << std::endl;}
};

template<typename T>
struct DerivedWithBarT : public T
{
    template <typename U> void bar(U&&){std::cout << "DerivedWithBar::bar" << std::endl;}
};

using DerivedWithBar = DerivedWithBarT<BaseWithFoo>;

template<typename T>
struct DerivedWithFooT : public T
{
    template <typename U> void foo(U&&){std::cout << "DerivedWithFoo::foo" << std::endl;}
};

using DerivedWithFoo = DerivedWithFooT<BaseWithFoo>;

template<typename T>
struct EmptyDerivedT : public T {};

using EmptyDerived = EmptyDerivedT<BaseWithFoo>;

struct BaseWithBar
{
    template <typename T> void bar(T&&){std::cout << "BaseWithBar::bar" << std::endl;}
};

struct EmptyBase {};

template<typename...>
using void_t = void;

template<typename, typename = void_t<>>
struct HasFoo: std::false_type {};

template<typename T, template<typename> class U>
struct HasFoo<U<T>, void_t<decltype(&U<EmptyBase>::template foo<T>)>>: std::true_type {};

template<typename, typename = void_t<>>
struct HasBar: std::false_type {};

template<typename T, template<typename> class U>
struct HasBar<U<T>, void_t<decltype(&U<EmptyBase>::template bar<T>)>>: std::true_type {};

template<typename T>
struct BarOverFoo {
    static constexpr bool value = HasBar<T>::value && !HasFoo<T>::value;
};

template<typename T>
struct FooOverBar {
    static constexpr bool value = HasFoo<T>::value && !HasBar<T>::value;
};

template<typename T>
struct Both {
    static constexpr bool value = HasFoo<T>::value && HasBar<T>::value;
};

template<typename T>
struct None {
    static constexpr bool value = !HasFoo<T>::value && !HasBar<T>::value;
};

struct Caller
{
    template <typename T>
    std::enable_if_t<FooOverBar<T>::value>
    call(T&& x)
    {
        x.foo(*this);
    }

    template <typename T>
    std::enable_if_t<BarOverFoo<T>::value>
    call(T&& x)
    {
        x.bar(*this);
    }

    template <typename T>
    std::enable_if_t<Both<T>::value>
    call(T&& x)
    {
        x.bar(*this);
    }

    template <typename T>
    std::enable_if_t<None<T>::value>
    call(T&& x)
    {
        callInternal(std::forward<T>(x));
    }

private:
    template <typename T>
    auto callInternal(T&& x) -> decltype(x.foo(*this), void())
    {
        x.foo(*this);
    }

    template <typename T>
    auto callInternal(T&& x) -> decltype(x.bar(*this), void())
    {
        x.bar(*this);
    }
};

int main()
{
    Caller c;
    c.call(BaseWithFoo());
    c.call(DerivedWithFoo());
    c.call(DerivedWithBar());
    c.call(EmptyDerived());
    c.call(BaseWithBar());
}

Adjust the call methods according with your requirements.
I've set a few defaults I'm not sure are fine.

查看更多
登录 后发表回答