2016-03-23 4 views
3

Ich versuche, über alle Basisklassen einer variadic abgeleiteten Klasse "iterieren" und rufen Sie eine Methode namens "Stream", wenn es existiert.mit sfinae zu erkennen, ob Basisklassen einer variadic Vorlage spezifische Methode haben

Um zu überprüfen, ob eine Methode existiert, benutze ich sfinae, und es funktioniert (siehe auskommentierten Code). Aber wenn ich es mit variadischer Vorlage "Iteration" kombiniere, wenn es nicht funktioniert, sieht der Fehler irgendwie so aus, als ob der Sfinae-Teil plötzlich nicht wie erwartet funktioniert, wenn er innerhalb der variadischen Magie ist.

Hilfe geschätzt. Ich benutze gcc 5.3.0.

#include <type_traits> 
#include <iostream> 

namespace detail{ 
    template<class> struct sfinae_true : std::true_type{}; 

    template<class T, class A0, class A1> static auto test_stream(int) -> sfinae_true<decltype(
      std::declval<T>().stream(std::declval<A0>(), std::declval<A1>()))>; 
    template<class, class A0, class A1> static auto test_stream(long) -> std::false_type; 
} 

template<class T, class A0, class A1> struct has_stream : decltype(detail::test_stream<T, A0, A1>(0)){}; 

struct X{ void stream(int, bool){} }; 
struct A{ void stream(int, bool){} }; 
struct Y{}; 

template <typename ... T> class Z : public T ... { 
    public: 
    void ff() { 
     std::initializer_list<bool> { 
      (has_stream<T,int,bool>() ? (T::stream(0, 0) , true) : false) ... 
     }; 
    } 
}; 

int main(){ 

    Z<X,A> a; 
    Z<X,A,Y> b; 

/* this works as expected. 
    // this runs 
    if (has_stream<X, int, bool>()) { 
     std::cout << "has int, bool" << std::endl; 
    } 
    // and this doesn't 
    if (has_stream<Y, int, long>()) { 
     std::cout << "has int long" << std::endl; 
    } 
*/ 

    a.ff(); // no error 
    b.ff(); // error 

} 


$ g++ --std=c++14 -O0 2.cpp                                      
2.cpp: In instantiation of ‘void Z<T>::ff() [with T = X, A, Y]’: 
2.cpp:41:10: required from here 
2.cpp:22:52: error: ‘stream’ is not a member of ‘Y’ 
      (has_stream<T,int,bool>() ? (T::stream(0, 0) , true) : false) ... 
                ^
2.cpp:21:9: error: no matching function for call to ‘std::initializer_list<bool>::initializer_list(<brace-enclosed initializer list>)’ 
     std::initializer_list<bool> { 
     ^
+0

Klingt wie ein [Konzepte] (https://en.wikipedia.org/wiki/Co ncepts_ (C% 2B% 2B)) Problem. – erip

+1

Könnten Sie bitte genauer sein? von dem, was ich von dem Artikel bekommen habe - Konzepte sind in C++ 14 nicht verfügbar. Ich sehe keinen Grund, warum mein Code nicht in C++ 14 funktionieren sollte (du machst mir keinen Vorwurf, aber ich selbst, versteh mich nicht falsch). –

+0

Ich schlage nicht vor, dass Ihr gewünschtes Verhalten im Bereich von C++ 14 unmöglich ist. Ich sage, dass dies ein guter Kandidat für Konzepte zu sein scheint, die derzeit (und in absehbarer Zukunft) nicht verfügbar sind. – erip

Antwort

4

Entweder Verwendung tag-Dispatching mit Ihrer Art Charakterzug zusammen:

void ff() 
{ 
    std::initializer_list<int> { 
     (call<T>(has_stream<T,int,bool>{}), 0)... 
    }; 
} 

template <typename U> 
void call(std::true_type) 
{ 
    U::stream(0, 0); 
} 

template <typename U> 
void call(std::false_type) {} 

oder verwenden Ausdruck SFINAE:

void ff() 
{ 
    std::initializer_list<int> { 
     (call<T>(0), 0)... 
    }; 
} 

template <typename U> 
auto call(int) -> decltype(U::stream(0, 0), void()) 
{ 
    U::stream(0, 0); 
} 

template <typename U> 
void call(char) {} 
+0

danke. Die Antwort von Jarod42 ist großartig, aber ich habe das als Lösung markiert, weil es mehr Optionen bietet. –

3

Beide Zweige sollten innerhalb ff gültig sein. Sie haben spezifische Methode hinzufügen um daran zu arbeiten:

template <typename Base, typename T> 
std::enable_if_t<has_stream<Base,int,bool>::value, bool> 
call_stream_if_any(T& obj) 
{ 
    obj.Base::stream(0, 0); 
    return true; 
} 

template <typename Base, typename T> 
std::enable_if_t<!has_stream<Base,int,bool>::value, bool> 
call_stream_if_any(T& obj) 
{ 
    return false; 
} 

Und dann

template <typename ... T> class Z : public T ... { 
    public: 
    void ff() { 
     static_cast<void>(std::initializer_list<bool> { 
      (call_stream_if_any<T>(*this)) ... 
     }); 
    } 
}; 

Demo

+0

danke. Es ist schade, dass Sie hier keine zwei Antworten als Lösung markieren können. –

0

Hier ist die Z Klasse mit einigen privaten Helfer structs:

#include <iostream> 
#include <type_traits> 

namespace detail { 
    template <typename> struct TrueIfIsAType : std::true_type {}; 

    template <typename T, typename... Args> 
    auto test (int) -> TrueIfIsAType< decltype(std::declval<T>().stream(std::declval<Args>()...)) >; 

    template <typename, typename...> 
    auto test (bool) -> std::false_type; 
} 

template <typename T, typename... Args> 
struct HasStreamMember : decltype(detail::test<T, Args...>(0)) {}; 

template <typename... Ts> 
struct Z : public Ts... { 
    void foo() { 
     const std::initializer_list<bool> a = {(HasStreamMember<Ts, int, bool>::value ? true : false)...}; 
     const int b[] = {(Check<Ts>::execute(this, 0, 0), 0)...}; // The fix 
     std::cout << a.size() << '\n'; // Do whatever with 'a'. 
     static_cast<void>(b); 
    } 
private: 
    template <typename T, typename = void> 
    struct Check { 
     static void execute (Z*, int, bool) {} // Do nothing. 
    }; 

    template <typename T> 
    struct Check<T, std::enable_if_t<HasStreamMember<T, int, bool>::value>> { 
     static void execute (Z* z, int a, bool b) { 
      z->T::stream(a, b); 
     }  
    }; 
}; 

// Test 
struct Thing { void stream(int, bool) {std::cout << "Thing stream\n";} }; 
struct Object { void stream(int, bool) {std::cout << "Object stream\n";} }; 
struct Widget {}; 

int main(){ 
    std::cout << std::boolalpha << HasStreamMember<Thing, int, bool>() << '\n'; // true 
    std::cout << HasStreamMember<Object, int, bool>() << '\n'; // true 
    std::cout << HasStreamMember<Object, Widget>() << '\n'; // false 

    Z<Thing, Object> a; 
    Z<Thing, Object, Widget> b; 

    a.foo(); // Thing stream Object stream 2 
    b.foo(); // Thing stream Object stream 3 
} 
Verwandte Themen