检查成员函数是否存在,是否为SFINAE继承

时间:2016-07-13 08:01:11

标签: c++ templates c++14 sfinae

如何检查成员函数是否存在且继承?

我需要这个解决以下示例的歧义:

类型具有foo()bar()成员函数。 Callercall为给定类型存在的那个。但是,DerivedWithBar会从foo()继承BaseWithFoo,但会定义自己的bar()。因此,Caller不知道要调用哪个函数。

我需要一种方法来为继承的foo提供非继承bar()优先级,但我不知道如何检查成员函数是否被继承。< / p>

#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

所需的输出:

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

2 个答案:

答案 0 :(得分:2)

我找到了一种通过比较成员函数指针类型来区分继承和非继承成员函数的方法。

以下是我的完整问题的部分解决方案(&#34;给予非继承的成员函数优先于继承的成员函数&#34;)。这只会调用非继承的foo或非继承的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

答案 1 :(得分:0)

使用一堆模板,我们可以得到它:

#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());
}

根据您的要求调整call方法 我设置了一些默认值,我不确定是不是很好。