2016-07-13 17 views
2

Wie kann ich prüfen, ob eine Mitgliedsfunktion existiert und nicht geerbt ist?Prüfen, ob Mitgliedsfunktion existiert und nicht für SFINAE vererbt wird

Ich brauche diese Zweideutigkeit für das folgende Beispiel zu beheben:

Ein Typ hat entweder eine foo() oder eine bar() Memberfunktion. Caller wird call derjenige, der für den angegebenen Typ existiert. DerivedWithBar erbt jedoch foo() von BaseWithFoo, definiert aber eigene bar(). Daher weiß Caller nicht, welche Funktion aufgerufen werden soll.

Ich würde einen Weg brauchen, um die nicht geerbte foo Vorrang vor der geerbten bar(), aber ich weiß nicht, wie man überprüft, ob eine Member-Funktion vererbt wird oder nicht.

#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

gewünschter Ausgang:

Base::foo 
DerivedWithFoo::foo 
DerivedWithBar::bar 
Base::foo 
BaseWithBar::bar 
+0

Kennen Sie die Signatur der zu testenden Funktion ('void U :: foo (Caller &)')? – Jarod42

+0

@ Jarod42 die Signatur ist wie Sie es geschrieben haben, aber es ist eine Template-Funktion, so muss es generische sein –

Antwort

2

I einen Weg gefunden, zwischen geerbt und nicht vererbten Elementfunktionen zu unterscheiden, indem Arten von Mitgliedsfunktionszeigern vergleichen.

Das folgende ist eine Teillösung für mein vollständiges Problem ("nicht geerbten Mitglied Funktionen Vorrang vor geerbten geben"). Dies ruft nur nicht geerbte foo oder nicht vererbte 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

Sie können * Priorität * hinzufügen, um den anderen Teil zu lösen: [Demo] (http: //coliru.stacked-crooked .com/a/b2e3e5512ef241c1). – Jarod42

+0

@ Jarod42 danke, ich habe gerade etwas ähnliches eingegeben (basierend auf [dieser Antwort] (http://stackoverflow.com/a/38283990/678093)). –

0

Mit einem Bündel von Vorlage können wir es bekommen, wie es folgt:

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

die call Methoden nach Ihren Wünschen anpassen.
Ich habe ein paar Standardeinstellungen festgelegt, ich bin mir nicht sicher, sind in Ordnung.

+0

kann ich leider nicht erwarten, dass die abgeleiteten Typen Vorlagen sind –