2016-07-13 2 views
2

멤버 함수가 존재하고 이 아닌지 확인하려면 어떻게해야합니까?이 상속되지 않았습니까? SFINAE에 멤버 함수가 있고 상속되지 않았는지 확인하십시오.

나는 다음과 같은 예를 들어 모호성을 해결하려면이 필요합니다

유형 중 하나가있는 foo() 또는 bar() 멤버 함수. Caller은 주어진 유형에 대해 존재하는 call입니다. 그러나 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 
+0

테스트 할 함수의 서명을 알고 있습니까 ('void U :: foo (Caller &)')? – Jarod42

+0

@ Jarod42 서명은 작성한대로 템플릿 기능이므로 generic이 필요합니다 –

답변

2

는 I 멤버 함수 포인터의 종류를 비교함으로써 상속 및 상속 멤버 함수를 구별 할 수있는 방법을 발견했다.

다음은 전체 문제 ("상속되지 않은 멤버 함수를 상속 된 멤버 함수보다 우선 적용")에 대한 부분적인 해결책입니다. 이 경우에만 상속되지 않는 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

다른 부분을 해결하기 위해 * priority *를 추가 할 수 있습니다 : [Demo] (http : //coliru.stacked-crooked .com/a/b2e3e5512ef241c1). – Jarod42

+0

@ Jarod42 덕분에, 나는 ([이 답변] (http://stackoverflow.com/a/38283990/678093)을 기반으로) 비슷한 것을 입력하고있었습니다. –

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 방법을 조정합니다.
몇 가지 기본값을 설정했습니다. 잘 모르겠습니다.

+0

불행히도 파생 형식이 템플릿이 될 것으로 기대할 수 없습니다. –

관련 문제