2012-07-03 15 views
6

Qual è un buon modo per decodificare l'eredità circolare qui?Curiosa eredità circolare con mix-in in C++

class Node { 
    // ... 
public: 
    list<Node*> neighbors() { /* ... */ } 
    void update() { } 
} 

template<class NodeType> 
class HasImportance : public virtual NodeType { 
    double m_importance = 0.0; 
public: 
    void receive_importance(double imp) { /* ... */ } 
    void give_importance() { 
     for (auto neighbor : this->neighbors()) 
     neighbor->receive_importance(m_importance /* ... */); 
    } 
}; 

class TrafficLight : public HasImportance<TrafficLight>, virtual Node { 
public: 
    list<TrafficLight*> neighbors() { ... } 
    void update() { give_importance(); /* ... */ } 
}; 

non riesce (GCC 4.7.0) perché TrafficLight è un tipo incompleto quando HasImportance cerca di ereditare da essa.

Il vero problema è che HasImportance deve conoscere il tipo restituito da neighbors(). Se HasImportance eredita da Node, allora pensa neighbors() restituisce un elenco di Node*, non TrafficLight*, e di conseguenza non sapere che può chiamare receive_importance() sulle voci. Simile problema se HasImportance non eredita affatto.

BTW, quello che sto cercando di fare è fare alcuni mix-in per aiutare a definire una varietà di diversi tipi di grafici facilmente e per testare unitamente ogni mix-in separatamente. Per esempio , dovrei essere in grado di definire la classe nodo per un grafico di semafori semplicemente scrivendo qualcosa come class TrafficLight : public HasImportance, HasState<3>, virtual Node { }.

Ho trovato tre modi per risolvere questo, ma sembrano tutti brutti. (1) static_cast<NodeType*>. (2) TrafficLight passa il suo this a HasImportance nel suo costruttore. In questo modo, HasImportance non ha bisogno di ereditare del tutto; memorizza semplicemente un puntatore su (ahem) stesso e il parametro modello fornisce il tipo del puntatore . (3) Fare Node un modello di classe, come questo:

template<class NodeType> 
class Node { 
public: 
    list<NodeType*> neighbors() { /* ... */ } 
} 

class TrafficLight : public HasImportance<Node<TrafficLight>> { /* ... */ } 

che compila e non introduce una copia gratuita di this, ma sembra ... un po 'troppo curioso.

C'è un odore di codice qui? Devo avvicinarmi a questi grafici in modo completamente diverso con ?

+11

Uso 'statico_cast (questo)' è * normale * in CRTP. – kennytm

+0

@KennyTM: Mi piacerebbe andare così lontano e dire che questa è la chiave per usare il CRTP – PlasmaHH

+0

Grazie. Mi preoccupo di usare static_cast, perché sembra che sto ignorando un segno (un "odore") che qualcosa di più profondo è sbagliato. Se è "normale" in CRTP, immagino che non resisterò così tanto. Questo è il mio primo CRTP. Puoi dire? :) –

risposta

1

(3) ma un po 'diverso.

template <class NodeType> 
class Node { ... }; 

template<class NodeType> 
class HasImportance : public virtual Node<NodeType> { ... }; 

class TrafficLight : public HasImportance<TrafficLight> { ... }; 

Mi sembra del tutto semplice, non più curioso del CRTP stesso.

+0

Grazie! Mi piace molto meglio anche se è una piccola differenza. L'interfaccia "compile-time" per i mix-in è ora semplice e piuttosto immune alle modifiche in altre parti del codice, a differenza della mia versione. –