views:

86

answers:

4

I have a custom container class for which I'd like to write the iterator and const_iterator classes.

I never did this before and I failed to find an appropriate how-to. What are the guidelines regarding iterator creation, and what should I be aware of ?

I'd also like to avoid code duplication (I feel that const_iterator and iterator share many things; should one subclass the other ?).

Thanks.

Foot note: I'm pretty sure Boost has something to ease this but I can't use it here, for many stupid reasons.

+2  A: 

I don't know if Boost has anything that would help.

My preferred pattern is simple: take a template argument which is equal to value_type, either const qualified or not. If necessary, also a node type. Then, well, everything kind of falls into place.

Just remember to parameterize (template-ize) everything that needs to be, including the copy constructor and operator==. For the most part, the semantics of const will create correct behavior.

template< class ValueType, class NodeType >
struct my_iterator
 : std::iterator< std::bidirectional_iterator_tag, T > {
    ValueType &operator*() { return cur->payload; }

    template< class VT2, class NT2 >
    friend bool operator==
        ( my_iterator const &lhs, my_iterator< VT2, NT2 > const &rhs );

    // etc.

private:
    NodeType *cur;

    friend class my_container;
    my_iterator( NodeType * ); // private constructor for begin, end
};

typedef my_iterator< T, my_node< T > > iterator;
typedef my_iterator< T const, my_node< T > const > const_iterator;
Potatoswatter
I never knew it was that simple. Thanks.
ereOn
Note: it looks like your conversions iterator->const_iterator and back are broken.
Maxim Yegorushkin
@Maxim: Yes, I can't actually find any examples of using my technique :vP . I'm not sure what you mean the conversions are broken, since I simply didn't illustrate them, but there might be an issue accessing `cur` from the iterator of opposite constness. The solution that comes to mind is `friend my_container::const_iterator; friend my_container::iterator;`, but I don't think that's how I did it before… anyway this general outline works.
Potatoswatter
* make that `friend class` in both cases.
Potatoswatter
+4  A: 
Andrey
Whoever downvoted this, I gave an upvote back… that's not cool.
Potatoswatter
Thanks. Very clear answer.
ereOn
+1  A: 

They often forget that iterator must convert to const_iterator but not the other way around. Here is a way to do that:

template<class T, class Tag = void>
class IntrusiveSlistIterator
   : public std::iterator<std::forward_iterator_tag, T>
{
private:
    typedef SlistNode<Tag> Node;
    Node* node_;

public:
    IntrusiveSlistIterator(Node* node);

    T& operator*() const;
    T* operator->() const;

    IntrusiveSlistIterator& operator++();
    IntrusiveSlistIterator operator++(int);

    bool operator==(IntrusiveSlistIterator b) const;
    bool operator!=(IntrusiveSlistIterator b) const;

    // one way conversion: iterator -> const_iterator
    operator IntrusiveSlistIterator<T const, Tag>() const;
};

In the above notice how IntrusiveSlistIterator<T> converts to IntrusiveSlistIterator<T const>. If T is already const this conversion never gets used.

Maxim Yegorushkin
Actually, you can also do it the other way around by defining a copy constructor that is template, it won't compile if you try to cast the underlying type from `const` to non-`const`.
Matthieu M.
Won't you end up with an invalid `IntrusiveSlistIterator<T const, void>::operator IntrusiveSlistIterator<T const, void>() const`?
Potatoswatter
Ah, it is valid, but Comeau gives a warning and I suspect a lot of others will as well. An `enable_if` might fix it, but…
Potatoswatter
I did not bother with enable_if because the compiler disables it anyway, although some compilers give a warning (g++ being a good boy does not warn).
Maxim Yegorushkin
@Matthieu: If one goes with a template constructor, when converting const_iterator to iterator the compiler produces an error inside the constructor, making the user scratch his head in confusion and utter wtf. With the conversion operator I posted, the compiler just says that there is no suitable conversion from const_iterator to iterator, which, IMO, is more clear.
Maxim Yegorushkin
@Maxim: I hadn't thought about a better diagnostic, that would be interesting. It would be great if the warning could be fixed because I usually compile with warnings considered as errors, but I'll keep this in mind.
Matthieu M.
+3  A: 

Boost has something to help: the Boost.Iterator library.

More precisely this page: boost::iterator_adaptor.

What's very interesting is the Tutorial Example which shows a complete implementation, from scratch, for a custom type.

The main point, as has been cited already, is to use a single template implementation and typedef it.

Matthieu M.
+1 Definitely the only way to go.
Sebastian