【问题标题】:Check if member function exists and is not inherited for SFINAE检查成员函数是否存在并且没有被 SFINAE 继承
【发布时间】:2016-07-13 08:01:10
【问题描述】:

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

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

一个类型要么有一个foo() 要么有一个bar() 成员函数。 Callercall 为给定类型存在的那个。然而,DerivedWithBarBaseWithFoo 继承foo(),但定义了自己的bar()。因此,Caller 不知道调用哪个函数。

我需要一种方法让非继承的 foo 优先于继承的 bar(),但我不知道如何检查成员函数是否被继承。

#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

【问题讨论】:

  • 你知道要测试的函数的签名(void U::foo(Caller&amp;))吗?
  • @Jarod42 签名和你写的一样,但是它是一个模板函数,所以它需要是通用的

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


【解决方案1】:

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

以下是我的全部问题的部分解决方案(“赋予非继承的成员函数优先于继承的成员函数”)。这只会调用非继承的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

【讨论】:

  • 您可以添加优先级来解决其他部分:Demo.
  • @Jarod42 谢谢,我只是在输入类似的内容(基于this answer)。
【解决方案2】:

通过一堆模板,我们可以得到如下:

#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 方法。
我设置了一些默认值,我不确定是否可以。

【讨论】:

  • 不幸的是,我不能指望派生类型是模板
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多