I have a situation where I fall foul of the aforementioned issue. I would like to construct a tree with associated nodes. Since all trees will behave the same but with different types, in the spirit of inheritance I would like to be able to construct trees with different types using the same tree construction routine defined by the base class. I would like to know what the best practice is for my situation.
struct Tree
{
struct Node { std::list<std::shared_ptr<Node>> children_; };
Tree()
{
root_ = CreateNode();
// carry on adding nodes to their parents...
}
virtual std::shared_ptr<Node> CreateNode() { return std::shared_ptr<Node>(new Node()); }
std::shared_ptr<Node> root_;
};
struct TreeDerived : public Tree
{
struct NodeDerived : public Tree::Node {};
TreeDerived() : Tree() {}
virtual std::shared_ptr<Node> CreateNode() { return std::shared_ptr<NodeDerived>(new NodeDerived()); }
};
The problem is that I cannot call derived functions until the base is constructed (obviously), and that uses the base implementation of the CreateNode
method which always constructs trees with the base node implementation. This means I can construct trees without delaying tree population. The obvious solution is to template the tree to take different node types, enforcing the node type using traits? However, this also means definition of all methods would have to be in the header? The class is quite meaty so I would like to avoid this if possible, so I thought about passing a lambda which does this for me.
struct Tree
{
struct Node { std::list<std::shared_ptr<Node>> children_; };
Tree(std::function<std::shared_ptr<Node>()> customNodeConstructor)
{
root_ = customNodeConstructor();
// carry on adding nodes to their parents... using the customNodeConstructor to create the nodes.
}
std::shared_ptr<Node> root_;
};
struct TreeDerived : public Tree
{
struct NodeDerived : public Tree::Node {};
TreeDerived(std::function<std::shared_ptr<Node>()> customNodeConstructor) : Tree(customNodeConstructor) {}
};
Which allows me to derive and pass a customNodeConstructor
relevant to the derived tree. Type safety is partially enforced since the returned shared_ptr
object must derive from Tree::Node
, although isn't enforced to the derived node type.
i.e a TreeDerived
instantiation, which should perhaps use TreeDerived::NodeDerived
is only enforced to use Tree::Node
or derived type, but not necessarily TreeDerived::NodeDerived
.
This can then be used like so...
Tree tree([]() { return std::shared_ptr<Tree::Node>(); });
TreeDerived treeDerived([]() { return std::shared_ptr<TreeDerived::NodeDerived>(); });
Is this good practice or should I be doing more/something else without templating the Tree object?
Many thanks.