2012-07-03 2 views
6

Was ist eine gute Möglichkeit, die zirkuläre Vererbung hier zu entschlüsseln?Neugierige zirkuläre Vererbung mit Mix-Ins 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(); /* ... */ } 
}; 

Es schlägt fehl (gcc 4.7.0), weil TrafficLight ist ein unvollständiger Typ wenn HasImportance versucht davon zu erben.

Das eigentliche Problem ist, dass HasImportance den Typ kennen muss, der von neighbors() zurückgegeben wird. Wenn HasImportance erbt von Node, dann denkt, dass es neighbors() gibt eine Liste der Node*, nicht TrafficLight* und folglich nicht wissen, dass es receive_importance() auf die Elemente aufrufen können. Ähnlich Problem, wenn HasImportance überhaupt nicht erbt.

BTW, was ich versuche zu tun ist ein paar Mix-Ins zu helfen, eine Vielzahl von verschiedenen Arten von Graphen einfach zu definieren und jeden Mix-In einzeln zu testen. Für Beispiel sollte ich in der Lage sein, die Knoten-Klasse für ein Diagramm der Ampel zu definieren, indem Sie einfach so etwas wie class TrafficLight : public HasImportance, HasState<3>, virtual Node { } schreiben.

Ich habe drei Wege gefunden, um dies zu lösen, aber alle scheinen hässlich. (1) static_cast<NodeType*>. (2) TrafficLight übergibt seine this an HasImportance in seinem Konstruktor. Auf diese Weise muss HasImportance überhaupt nicht erben; Es speichert nur einen Zeiger auf (ähem) selbst, und der Vorlagenparameter liefert den Typ des Zeigers. (3) Stellen Sie Node eine Klassenvorlage, wie folgt aus:

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

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

Das kompiliert und es keine unentgeltlichen Kopie this, vorstellen, aber es scheint ... ein wenig zu neugierig.

Gibt es hier einen Code-Geruch? Sollte ich diese Graphen in einer komplett anderen Art und Weise angehen?

+11

Die Verwendung von 'static_cast (this)' ist * normal * in CRTP. – kennytm

+0

@KennyTM: Ich würde sogar so weit gehen und sagen, dass dies der Schlüssel bei der Verwendung des CRTP ist – PlasmaHH

+0

Danke. Ich verkneife mich bei der Verwendung von static_cast, weil es so aussieht, als ignoriere ich ein Zeichen (einen "Geruch"), dass etwas tiefer ist falsch. Wenn es in CRTP "normal" ist, werde ich nicht so sehr widerstehen. Dies ist mein erstes CRTP. Können Sie sagen? :) –

Antwort

1

(3) aber ein bisschen anders.

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

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

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

Sieht ganz einfach für mich aus, nicht neugieriger als das CRTP selbst.

+0

Danke! Ich mag das viel besser, obwohl es ein kleiner Unterschied ist. Die "Compile-Time-Schnittstelle" zu den Mix-Ins ist jetzt einfach und ziemlich immun gegen Änderungen an anderen Stellen im Code, im Gegensatz zu meiner Version. –