They then want to add it to their graph (which could also be derived FYI):
O_o
But I want to be able to put DerivedNodes in to that function to get their neighbours and that is where the problem is arising.
o_O
Your only problem is that you are trying to accomplish polymorphic collections and homogeneous collections with the same code.
Code:
struct MyDerivedNode: public BaseNode {/* ... */};
Code:
struct UltraDerivedNode: public DerivedNode {/* ... */};
Code:
DerivedGraph g;
// ...
DerviedNode * d(new DerivedNode);
g.addNode(d);
// ...
MyDerviedNode * m(new MyDerviedNode);
g.addNode(m);
// ...
UltraDerivedNode * u(new UltraDerivedNode);
g.addNode(u);
Code:
std::vector<BaseNode *> bList;
std::vector<DerivedNode *> dList;
std::vector<MyDerviedNode *> mList;
std::vector<UltraDerivedNode *> uList;
(You may assume some missing magic to allow the provided examples to work as you would like given your thread.)
Code:
g.getNeighbours(d, bList); // 1
g.getNeighbours(d, dList); // 2
g.getNeighbours(d, mList); // 3
g.getNeighbours(d, uList); // 4
Code:
g.getNeighbours(m, bList); // 5
g.getNeighbours(m, dList); // 6
g.getNeighbours(m, mList); // 7
g.getNeighbours(m, uList); // 8
Code:
g.getNeighbours(u, bList); // 9
g.getNeighbours(u, dList); // 10
g.getNeighbours(u, mList); // 11
g.getNeighbours(u, uList); // 12
Are any of the marked lines problematic with respect to behavioral expectations of the contained types?
Yes. Which lines aren't problematic with respect to behavioral expectations of the contained types?
The lines 1, 5, and 9 are good. The good lines yield a `std::vector<BaseNode *>' object containing only those classes derived from the `BaseNode' class.
The other lines are problem. Every other line yields a collection containing objects which do not conform to the expectations of the code using the containers. Code using `dList' will find a `MyDerivedNode' which isn't a descendant of `DerivedNode' so may be assumed to have different characteristics and interfaces. Code using `mList' will find a `UltraDerivedNode' and a `DerivedNode' both of which may be assumed to have different characteristics and interfaces. Code using `uList' will find a `DerivedNode' which even related may more stringent requirements or be missing required interfaces. Code using the problematic collections would be required to confirm that the type contained is actually of the type expected or risk undefined behavior. You should simply not lie about the properties of a collection; you will only cause yourself headache.
You can't even use the Visitor pattern suggested by laserlight to solve the problem; you can only abort early with an exception if the expectation of the client code would have failed.
You can use the strategy suggested multiple times; you can simply store a `BaseNode *' in the collection provided by the interface. Code expecting a particular descendant would still be required to confirm the type contained, but code written against the `BaseNode' class may simply use the collection which should be the majority of code.
*shrug*
I would advise returning an iterator so that the client may use whatever container they choose, but using the iterator pattern doesn't eliminate the problem of not telling the truth about the contents of a collection.
Soma