Pointers in const containers
Lets consider following (typical as I think) code:
class tree_node {
public:
typedef std::vector< tree_node* > children_vector;
const children_vector& get_children() const { return children_; }
children_vector& get_children() { return children_; }
const tree_node* get_parent() const { return parent_; }
tree_node* get_parent() { return parent_; }
private:
children_vector children_;
tree_node* parent_;
};
(Code was not compiled and important parts - like constructors and
destructor - are not present since they are not important for the
problem inhere.)
There is an obvious problem of constness. Lets say we have a variable
"p_node" of type "const tree_node*". The pointer is to a const object
so we would except the object not being modified using this pointer
(compiler ought to check that). But as it is not possible to write:
p_node->get_children().clear()
since get_children returns const reference it is possible to do
following:
p_node->get_children().front()->do_something_non_const();
p_node->get_children().back() = new tree_node();
p_node->get_children().front()->get_parent()-
do_something_non_const();
(assuming the vector is not empty). Note that the last instruction
calls "do_something_non_const" on object pointed by "p_node"...
This should not be possible for a const tree_node (or at least in my
opinion) and is possible only because of use of container (for
simplicity) and pointers (since tree_node cannot be used to
instantiate std::vector before it is a complete type and it is not a
complete type while defining
children_type).
How to solve this problem?
I already came to two solutions:
1) Do not return the container, but add functions like
"children_begin" and "children_end" and define iterators so that the
will be const or non-const as appropriate. This requires somewhat more
coding but on the other hand makes tree_node's interface independent
on the actual implementation of children_. But this solution does not
go well with code which requires container and not pair of iterators,
like inserter iterators, BOOST_FOREACH, legacy code and so on...
2) Make tree_node behave like it was a container of "tree_node*". This
implies a lot of writing, since we would have to cover the all
requirements placed on a container. But using the same trick with
defining const and non-const iterators we can solve problem of
modifying const object. And the object of "tree_node" behaves like a
vector so we can use insert iterators, BOOST_FOREACH and all those
things mentioned in 1. But this solution is not good when tree_node
has children of two types, for example if we want to distinguish
children being leafs and children not being leafs. Since then
tree_node is a container of both other tree_nodes and appropriate
children.
So is there another solution?
Adam Badura
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]