Check if member function exists and is not inherited for SFINAE

。_饼干妹妹 提交于 2019-12-04 17:40:36

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

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.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!