tags:

views:

65

answers:

3

I'm implementing map for an excersise and I'm at the point where I would have to iterate over it (done and working) but the problem is that I don't know how to implement a last element (presumably an empty link). I was thinking that I would attach some special kind of link (descendant of base link) which would then be attached to the last element and in this way I would be able to detect if I'm at the real last element or not. I wonder what will be your opinion about this idea and probably hear from you about some more traditional and regularly used techniks for doing this.

+3  A: 

There are two ways to implement end: a dummy node and a singular iterator (i.e. NULL).

The problem with NULL is that you need to be able to get back to the structure if you decrement --end().

What GCC does is create a truncated dummy node in the map object itself. It has links but no data payload. It is maintained as the root of the tree, with a circular parent link that invokes special behavior. Semantically it might be a little messy, but C++-compliant semantics should be worth the hassle.

Potatoswatter
@Potatoswatter I don't quite understand what do you mean by singular iterator. Could you please explain that? Thank you.
smallB
It cannot be a NULL pointer. How would operator `--` work ?
adf88
@afd88: deleted shortly after your comment, edited.
Potatoswatter
@smallB: in cases where `--` does not apply, such as stream iterators, you can just put the value `0` inside the iterator. In that case, the natural `end` of any structure is equal to the default value of the iterator.
Potatoswatter
@Potatoswatter so it is basically what I've had in mind talking about "special kind" of link isn't that so?
smallB
Potatoswatter
@Potatoswatter thanks for links.
smallB
+2  A: 

If your iterator isn't bidirectional then you don't really need end to point to anything (just use NULL in that case). end() only needs to have a real value in the case of a bidirectional iterator, because in that case you'll need to be able to move backwards from end() towards the beginning of the list.

The GNU C++ Library (what you get if you use std::map with GCC/G++) implements end() as a pointer to the root node of the tree. This way, if used in a bidirectional iterator you can access the root node to find the right most node in the tree (which is the node directly before end()).

Edit to explain an empty tree

The map itself always contains a node for the root of the tree (it's not a pointer, its a normal member node). When the tree is empty, both leaves of this node point to the node itself (as does end()). So in an empty tree begin() would return the same thing as end().

So we have something like this:

template<class T> struct node_t {
    T data;
    node_t *left;
    node_t *right;
};


template<class T> class map {
    private:
        node_t root;
    public:
        // ...
        iterator begin() { return iterator(root.left); }
        iterator end() { return iterator(&root); }
}
SoapBox
So what if we have one node? begin == end? Could you explain more? Do you mean to use dummy node as the root?
adf88
The root node always exists, when the tree is empty the root node's left and right leaves are both NULL. So in an empty tree, begin() would point to the left leaf (which is NULL), and end would still point to the root node.
SoapBox
I'll edit the answer to explain a little more.
SoapBox
@SoapBox thanks for your example. But what will happen if you will try to dereference begin? Won't you get runtime error? Begin is a null.
smallB
I believe at initialization, the leaves should both point to the root, but +1 anyway.
Potatoswatter
In an empty tree, yes. But you shouldn't dereference being in an empty tree, because it's empty.
SoapBox
Ah, you're right @Potatoswatter.
SoapBox
@SoapBox but isn't this method somewhat incompatibile with std approach where end points to "one past last"?
smallB
This is the std approach, as used by GCC 4.4.1. It doesn't physically point to one past last, but the code checks for this condition and treats it the same way as a one-past-last would. The problem with having a literal one-past-last is you have to take care that someone doesn't delete that node or move it or try to insert something after it, etc.
SoapBox
@smallB: One past the rigthmost node will ascend up to the root. Then you need to detect the circular link to prevent infinite recursion. (Some kind of root parent detection is necessary no matter what.)
Potatoswatter
@SoapBox and Potatoswatter another question arises when I think about root being as a "one past last" node (I like this idea BTW). Special care has to be taken when inserting and deleting, isn't that so?
smallB
Yes, but you'd have to take special care when inserting and deleting anyway, to make sure that your one-past-last node is still working correctly.
SoapBox
A: 

Use dummy node for that.

adf88