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