2011-11-03 7 views
9

Ho un problema in cui ho bisogno di scoprire l'antenato comune di due tipi (con una o zero classi di base) se esiste. È possibile costruire un tratto di tipo per risolvere questo problema? In Codice:Come dedurre, in fase di compilazione, la radice di un albero ereditario comune a due tipi se ne esiste uno?

template<typename T1, typename T2> 
    struct closest_common_ancestor 
{ 
    typedef XXX type; // what goes here? 
}; 

Dati i seguenti tipi:

struct root {}; 
struct child1 : root {}; 
struct child2 : root {}; 
struct child3 : child2 {}; 
struct unrelated {}; 

closest_common_ancestor comporterebbe nei seguenti tipi:

closest_common_ancestor<root, child1>::type == root 
closest_common_ancestor<child1, child2>::type == root 
closest_common_ancestor<child3, child1>::type == root 
closest_common_ancestor<child3, child2>::type == child2 
closest_common_ancestor<unrelated, child1>::type == error 

credo di poter risolvere questo problema se posso controllare se un il tipo ha zero o una classe base e, in tal caso, il nome di quel tipo. È possibile?

+2

Non è possibile esaminare le classi di base. A meno che tu non aggiunga manualmente le meta informazioni a ciascuna delle tue classi. –

+0

Si noti che per una relazione diretta è già possibile ('is_base_of' può essere implementato in termini di blocchi C++ 03 di base) –

+0

Se ci sono 2 root' root1' e 'root2', e' child1', 'child2' eredita entrambi ('struct child1: root1, root2 {};'), sarà ambiguo per 'closest_common_ancestor' cosa restituire. – kennytm

risposta

7

Come ha detto K-ballo è purtroppo impossibile ottenere l'elenco delle basi di una classe (troppo brutto ...).

Se si annota manualmente le classi (ad esempio, definire un semplice std::tuple<> elencando le basi), è possibile utilizzare queste informazioni. Il più semplice, ovviamente, potrebbe essere quella di utilizzare un tratto:

template <typename> struct list_bases { typedef std::tuple<> type; }; 

Quindi è possibile specializzarsi questa caratteristica per i tipi:

template <> struct list_bases<child1> { typedef std::tuple<root> type; }; 

E partendo da lì, si può cominciare esperimento di trovare un antenato ... tuttavia potrebbe non essere immediato. A parte i dettagli di implementazione (ottenendo le basi in modo ricorsivo, implementando la selezione "distanza"), mi aspetto un problema con casi "strani".

La selezione distanza può, in usuale gerarchia di ereditarietà (lineare) essere risolto utilizzando una combinazione di is_base_of e is_same tuttavia considerare la seguente gerarchia:

struct root1 {}; struct root2 {}; 

struct child1: root1 {}; struct child2: root2 {}; 

struct child12: root1, child2 {}; struct child21: root2, child1 {}; 

Ora, child12 e child21 hanno due antenati comuni: root1 e root2 ... qual è il più vicino?

Sono equivalenti. Si consideri che aggiungo:

struct root3 {}; struct child31: root3, child1 {}; 

Poi root1 è un antenato comune a child12, child21 e child31.

Tuttavia, se costeggiai sulla definizione di closest_common_ancestor e arbitrariamente definiti che closest_common_ancesotr<child12, child21> è root2, quindi non ho potuto trovare alcun antenato comune con child31.

La mia proposta è quindi di elencare tutti gli antenati più vicini e utilizzare tuple per implementare le operazioni di set.

+0

Grazie. Per la mia applicazione risolverei l'ambiguità che hai menzionato insistendo sul fatto che tutti i tipi in esame hanno solo zero o un tipo di base. –

Problemi correlati