What is a good way to unscramble the circular inheritance here?
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(); /* ... */ }
};
It fails (gcc 4.7.0) because TrafficLight is an incomplete type
when HasImportance tries to inherit from it.
The real problem is that HasImportance needs to know the type returned by
neighbors(). If HasImportance inherits from
Node, then it thinks neighbors() returns a list of
Node*, not TrafficLight*, and consequently doesn't
know that it can call receive_importance() on the items. Similar
problem if HasImportance doesn't inherit at all.
BTW, what I'm trying to do is make a few mix-ins to help define a variety of
different kinds of graphs easily and to unit-test each mix-in separately. For
example, I should be able to define the node class for a graph of traffic lights by just writing
something like class TrafficLight : public HasImportance, HasState<3>,
virtual Node { }.
I've come up with three ways to solve this, but all seem ugly. (1)
static_cast<NodeType*>. (2) TrafficLight passes its
this to HasImportance in its constructor. This way,
HasImportance doesn't need to inherit at all; it just stores a
pointer to (ahem) itself, and the template parameter provides the type of the
pointer. (3) Make Node a class template, like this:
template<class NodeType>
class Node {
public:
list<NodeType*> neighbors() { /* ... */ }
}
class TrafficLight : public HasImportance<Node<TrafficLight>> { /* ... */ }
That compiles and it doesn't introduce a gratuitous copy of this,
but it seems…a little too curious.
Is there a code smell here? Should I approach these graphs in a completely different way?
(3) but a bit differently.
template <class NodeType>
class Node { ... };
template<class NodeType>
class HasImportance : public virtual Node<NodeType> { ... };
class TrafficLight : public HasImportance<TrafficLight> { ... };
Looks entirely straightforward to me, not more curious than the CRTP itself.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With