2012-07-03 29 views
6

¿Cuál es una buena forma de descifrar la herencia circular aquí?Curiosa herencia circular con combinaciones en 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(); /* ... */ } 
}; 

falla (gcc 4.7.0) porque TrafficLight es un tipo incompleto cuando HasImportance intenta heredar de ella. El problema real es que HasImportance necesita saber el tipo devuelto por neighbors(). Si HasImportance hereda de Node, entonces se piensa neighbors() devuelve una lista de Node*, no TrafficLight*, y por consiguiente no sabe que puede llamar receive_importance() en los artículos. Problema similar si HasImportance no hereda en absoluto.

Por cierto, lo que estoy tratando de hacer es hacer algunas mezclas para ayudar a definir una variedad de diferentes tipos de gráficos fácilmente y para probar la unidad de cada mezcla por separado. Para el ejemplo , debería ser capaz de definir la clase de nodo para un gráfico de semáforos simplemente escribiendo algo así como class TrafficLight : public HasImportance, HasState<3>, virtual Node { }.

He encontrado tres formas de resolver esto, pero todo parece feo. (1) static_cast<NodeType*>. (2) TrafficLight pasa su this a HasImportance en su constructor. De esta manera, HasImportance no necesita heredar en absoluto; simplemente almacena un puntero en (ejem) sí mismo, y el parámetro de plantilla proporciona el tipo del puntero . (3) Hacer Node una plantilla de clase, así:

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

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

que compila y no introduce una copia gratuita de la this, pero parece ... un poco demasiado curioso.

¿Hay un olor a código aquí? ¿Debo acercarme a estos gráficos de una manera diferente por completo ?

+11

Usando 'static_cast (this)' es * normal * en CRTP. – kennytm

+0

@KennyTM: incluso iría tan lejos y diría que esta es la clave para usar el CRTP – PlasmaHH

+0

Gracias. Me estremezco de usar static_cast, porque parece que estoy ignorando un signo (un "olor") de que algo más profundo está mal. Si es "normal" en CRTP, supongo que no me resistiré tanto. Este es mi primer CRTP. ¿Puedes decir? :) –

Respuesta

1

(3) pero un poco diferente.

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

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

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

Parece completamente sencillo para mí, no es más curioso que el propio CRTP.

+0

Gracias! Me gusta mucho mejor a pesar de que es una pequeña diferencia. La "interfaz en tiempo de compilación" para las mezclas es ahora simple y bastante inmune a los cambios en otras partes del código, a diferencia de mi versión. –