2012-10-17 13 views
11

Ho provato a cercarlo su google senza fortuna, quindi ci sto provando.C++ tipi annidati in modo ricorsivo e nome iniezione

Ho un paio di classi, ognuna delle quali definisce un membro struct foo. Questo tipo di membro foo può ereditarsi da una delle classi precedenti, ottenendo quindi un tipo di membro foo stesso.

voglio accedere ai annidate foo tipi utilizzando il modello metaprogrammazione (vedi sotto), ma c nome ++ iniezione presenta problemi, come la parte superiore del foo nome del tipo viene iniettato il tipo inferiore foo, e quello superiore viene risolto quando voglio per accedere alla parte inferiore, ad esempio utilizzando A::foo::foo.

Ecco un esempio:

#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)"); 

proposito, sto implementando derivati ​​funzionali, foo è il tipo derivato. La situazione sopra riportata avviene ad es. con sin/cos.

TLDR: come ottengo foo<foo<A>> per essere foo<B>, non foo<A>?

Grazie!

risposta

1

Questa soluzione non è veramente automatica ma risolve il problema. I tuoi tipi forniscono un typedef alla classe base, l'assenza/presenza di questo typedef viene rilevata tramite SFINAE e il foo nidificato viene trovato sia attraverso la base o attraverso la normale ricerca.

È possibile automatizzare lo has_base per controllare un elenco di note basi con is_base_of se è necessaria più automazione.

#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

ho avuto qualcosa di simile in mente, ma era un po 'sperando di poter evitare di definire il tipo di base membro per ogni tipo derivato ... Grazie mille in ogni modo! – max

+0

@MaximeTournier Come ho detto: se l'elenco delle basi è corretto, puoi gestirle automaticamente. Di fronte all'eredità multipla questo diventa piuttosto strano. – pmr

+0

Capito, ma l'elenco delle basi non è stato risolto: -/ – max