2012-10-17 16 views
11

Próbowałem googling to bez powodzenia, więc próbuję tutaj.C++ rekursywnie zagnieżdżone typy i nazwy wtrysku

Mam kilka klas, z których każda definiuje członka struct foo. Ten typ członkowski foo może sam dziedziczyć z jednej z poprzednich klas, uzyskując sam element członkowski typu foo.

Chcę uzyskać dostęp do zagnieżdżonego foo rodzajów użyciu szablonu metaprogramowanie (patrz poniżej), ale zastrzyk C++ nazwa wprowadza problemy, jak górna foo nazwa typu zostaje wstrzyknięty do dolnej foo typu, a górna zostanie rozwiązany, kiedy chcę aby uzyskać dostęp do niższego, powiedzmy, używając A::foo::foo.

Oto przykład:

#include <type_traits> 

struct A; 
struct B; 

struct A { 
    struct foo; 
}; 

struct B { 
    struct foo; 
}; 

struct A::foo : B { }; 
struct B::foo : A { }; 

// handy c++11 shorthand 
template<class T> 
using foo = typename T::foo; 

static_assert(std::is_same< foo< foo<A> >, foo<B> >::value, 
       "this should not fail (but it does)"); 

static_assert(std::is_same< foo< foo<A> >, foo<A> >::value, 
       "this should fail (but it does not)"); 

FYI, m wykonawczych pochodnych funkcyjnych foo jest typu pochodnej. Powyższa sytuacja ma miejsce np. z sin/cos.

TLDR: Jak mogę dostać foo<foo<A>> być foo<B>, nie foo<A>?

Dzięki!

Odpowiedz

1

To naprawdę nie jest automatyczne rozwiązanie, ale rozwiązuje problem. Twoje typy dostarczają typedef do klasy bazowej, nieobecność/obecność tego typedef jest wykrywana przez SFINAE, a zagnieżdżone foo znajduje się przez bazę lub przez normalny look-up.

Możesz prawdopodobnie zautomatyzować has_base, aby sprawdzić listę znanych baz z is_base_of, jeśli potrzebujesz więcej automatyzacji.

#include <type_traits> 
template <typename T> 
struct has_base 
{ 
    typedef char yes[1]; 
    typedef char no[2]; 

    template <typename C> 
    static yes& test(typename C::base*); 

    template <typename> 
    static no& test(...); 

    static const bool value = sizeof(test<T>(0)) == sizeof(yes); 
}; 

struct A { 
    struct foo; 
}; 

struct B { 
    struct foo; 
}; 

struct A::foo : B { typedef B base; }; 
struct B::foo : A { typedef A base; }; 

template<typename T, bool from_base = has_base<T>::value > 
struct foo_impl { 
    typedef typename T::base::foo type; 
}; 

template<typename T> 
struct foo_impl<T, false> { 
    typedef typename T::foo type; 
}; 

template<typename T> 
using foo = typename foo_impl<T>::type; 

static_assert(std::is_same< foo< foo<A> >::, foo<B> >::value, 
       "this should not fail (but it does)"); 

static_assert(std::is_same< foo< foo<A> >, foo<A> >::value, 
       "this should fail (but it does not)"); 
int main() 
{ 

    return 0; 
} 
+0

miałem coś podobnego w umyśle, ale był rodzaj nadzieją, że mogę uniknąć definiowania typu członu podstawy dla każdego typu pochodzącego ... dziękuję tak czy inaczej! – max

+0

@MaximeTournier Jak już powiedziałem: Jeśli lista baz jest ustalona, ​​możesz obsługiwać je automatycznie. W obliczu wielokrotnego dziedziczenia robi się to dość dziwne. – pmr

+0

Rozumiem, ale lista baz niestety nie jest ustalona: -/ – max