2012-07-03 23 views
6

Cách tốt nhất để xắp xếp lại thừa kế tròn ở đây là gì?Kế thừa tròn tò mò với các kết hợp trong 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(); /* ... */ } 
}; 

Nó thất bại (gcc 4.7.0) vì TrafficLight là một loại không đầy đủ khi HasImportance cố gắng để kế thừa từ nó.

Vấn đề thực sự là HasImportance cần phải biết loại trả về neighbors(). Nếu HasImportance thừa hưởng từ Node, sau đó nó nghĩ neighbors() trả về một danh sách các Node*, không TrafficLight*, và hậu quả là không biết rằng nó có thể gọi receive_importance() vào các mục. Tương tự vấn đề nếu HasImportance không kế thừa chút nào.

BTW, những gì tôi đang cố gắng thực hiện là một vài kết hợp để giúp xác định nhiều loại khác nhau của các loại đồ thị khác nhau một cách dễ dàng và đơn vị kiểm tra từng kết hợp riêng biệt. Đối với ví dụ , tôi có thể xác định lớp nút cho biểu đồ đèn giao thông bằng cách chỉ viết thứ gì đó như class TrafficLight : public HasImportance, HasState<3>, virtual Node { }.

Tôi đã đưa ra ba cách để giải quyết vấn đề này, nhưng tất cả đều có vẻ xấu. (1) static_cast<NodeType*>. (2) TrafficLight chuyển số this tới HasImportance trong hàm tạo của nó. Bằng cách này, HasImportance không cần phải kế thừa; nó chỉ lưu trữ một con trỏ đến (ahem) và tham số mẫu cung cấp loại con trỏ . (3) Thực hiện Node một lớp mẫu, như thế này:

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

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

Đó biên dịch và nó không giới thiệu một bản sao nhưng không của this, nhưng có vẻ như ... một chút quá tò mò.

Có mùi mã ở đây không? Tôi có nên tiếp cận các biểu đồ này theo cách khác hoàn toàn không?

+11

Sử dụng 'static_cast (điều này)' là * bình thường * trong CRTP. – kennytm

+0

@KennyTM: Tôi thậm chí sẽ đi xa và nói rằng đây là chìa khóa trong việc sử dụng CRTP – PlasmaHH

+0

Cảm ơn bạn. Tôi cringe lúc sử dụng static_cast, bởi vì nó có vẻ như tôi đang bỏ qua một dấu hiệu (một "mùi") mà cái gì sâu sắc hơn là sai. Nếu nó là "bình thường" trong CRTP, tôi đoán tôi sẽ không cưỡng lại quá nhiều. Đây là CRTP đầu tiên của tôi. Bạn có thể nói? :) –

Trả lời

1

(3) nhưng hơi khác một chút.

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

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

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

Có vẻ hoàn toàn đơn giản đối với tôi, không tò mò hơn chính CRTP.

+0

Cảm ơn! Tôi thích điều này tốt hơn rất nhiều mặc dù đó là một sự khác biệt nhỏ. "Giao diện thời gian biên dịch" cho các bản mix giờ đây đơn giản và khá miễn dịch với những thay đổi ở nơi khác trong mã, không giống như phiên bản của tôi. –