2012-07-03 11 views
6

Jaki jest dobry sposób na rozszyfrowanie tutaj dziedziczenia kołowego?Ciekawe okrągłe dziedziczenie z wymieszaniem w 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(); /* ... */ } 
}; 

To nie powiedzie (gcc 4.7.0), ponieważ TrafficLight jest niekompletny typ gdy HasImportance próbuje dziedziczą z niego.

Prawdziwy problem polega na tym, że firma HasImportance musi znać typ zwracany przez neighbors(). Jeśli HasImportance dziedziczy z Node, to uważa neighbors() Zwraca listę Node* nie TrafficLight*, a co za tym idzie nie wiedzieć, że można to nazwać receive_importance() na pozycje. Podobne problem, jeśli HasImportance nie dziedziczy w ogóle.

BTW, próbuję zrobić kilka miksów, aby pomóc zdefiniować różne rodzaje wykresów z łatwością i osobno przetestować każdy mix-in. Dla przykładu, powinienem móc zdefiniować klasę węzłów dla wykresu świateł drogowych, pisząc po prostu coś w rodzaju class TrafficLight : public HasImportance, HasState<3>, virtual Node { }.

Wymyśliłem trzy sposoby rozwiązania tego problemu, ale wszystkie wydają się brzydkie. (1) static_cast<NodeType*>. (2) TrafficLight przekazuje swój do w swoim konstruktorze. W ten sposób, HasImportance nie musi w ogóle dziedziczyć; po prostu przechowuje wskaźnik do samego (ahem), a parametr szablonu określa typ wskaźnika . (3) Sprawdź Node szablonu klasy, takie jak to:

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

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

To kompiluje i nie wprowadza nieuzasadnioną kopię this, ale wydaje się ... trochę zbyt ciekawy.

Czy jest tu zapach kodu? Czy powinienem podchodzić do tych wykresów w zupełnie inny sposób?

+11

Używanie 'static_cast (this)' jest * normalne * w CRTP. – kennytm

+0

@KennyTM: Posunąłbym się tak daleko i powiem, że to jest klucz do korzystania z CRTP – PlasmaHH

+0

Dzięki. Skłaniam się do używania static_cast, ponieważ wydaje mi się, że ignoruję znak ("zapach"), że coś głębszego jest nie tak. Jeśli jest to "normalne" w CRTP, myślę, że nie będę się opierać tak bardzo. To jest mój pierwszy CRTP. Możesz mi powiedzieć? :) –

Odpowiedz

1

(3) ale nieco inaczej.

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

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

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

Wygląda zupełnie oczywiste dla mnie, nie bardziej ciekawa niż sam CRTP.

+0

Dzięki! Podoba mi się to znacznie lepiej, mimo że to mała różnica. "Interfejs czasu kompilacji" do wtyczek jest teraz prosty i dość odporny na zmiany w innym miejscu kodu, w przeciwieństwie do mojej wersji. –