views:

219

answers:

5

OK, so I have two (completely unrelated, different project) classes using iterators now. One has iterator and reverse_iterator working as intended, and the other, current one has iterator and a semi-broken const_iterator (specifically, because const_iterator derives from iterator, the code LinkedList<int>::iterator i = const_list.begin() is valid and allows you to modify the const defined list...).
I intend to add all four types to this class... If I can.

How would I proceed to minimize copy/pasting code and changing only the return type? Create a base class like base_iterator to inherit from? Create an iterator or const_iterator and inherit from that? Inherit from some std:: class? If any of these cases are the "best" approach, what code goes where?
Perhaps none of the alternatives are good? I'm quite lost here, and can't find much reference material.

Any advice is appreciated, but please keep in mind that I'm new to the subject (both iterators and C++ in general, especially OOP). I've tried, in vain, to study the header files shipped with GCC - they're not exactly the tutorial I'm looking for.

+3  A: 

Sometimes, blanket application of the so-called DRY rule (Don't Repeat Yourself, for those who aren't familiar) is not the best approach. Especially if you're new to the language (C++ and iterators) and OOP itself (methodology), there's little benefit in trying to minimise the amount of code you need to write right now.

I would implement the two iterators using appropriate code for each of them. Perhaps after you have more experience with the language, tools, and techniques, then go back and see whether you can reduce the amount of code by factoring out common code.

Greg Hewgill
Have you had to repeat the definition of DRY so much that you do it reflexively now? :P
Roger Pate
Probably sound advice, and I took it. I made two separate base classes, iterator and const_iterator (both subclasses of std::iterator), and based reverse_iterator on iterator, and const_reverse_iterator on reverse_iterator. The total line count for the iterators came to ~70-75. Not bad for 4 different types. That's excluding the upcoming inline documentation, but including enough whitespace to make it readable. :)
A: 

LinkedList<int>::iterator i = const_list.begin() What does your begin method look like? By studying the STL you can see that containers define two such methods with the following signatures:

const_iterator begin() const;
iterator begin();

You should not have a problem getting an iterator from an object qualified as const. I don't think DRY applies here.

Evän Vrooksövich
He currently (incorrectly) has const_iterator derive from iterator, so with the usual const/non-const overloads for begin, his code is still able to implicitly convert any const_iterator to an iterator.
Roger Pate
A: 
Roger Pate
A: 

Once I used the following approach:

  1. make a template class common_iterator
  2. add typedefs for "iterator" and "const_iterator"
  3. add to "common_iterator" a constructor taking "iterator" type

For "iterator" the additional constructor will replace default copy constructor, in my case it was equivalent to default copy constructor.

For "const_iterator" it will be an additional constructor which allows to construct "const_iterator" from "iterator"

maxim1000
+2  A: 

It's actually extremely simple.

First of all, take a look at Boost.Iterator library.

Second: you need to declare a base class (it's well explained in the example how to proceed) that will be similar to this one.

template <class Value>
class BaseIterator: boost::iterator_adaptor< ... > {};

You implement the operations to move your pointer around there. Note that because it's an adaptation over an already existing iterator, you can implement it with only a few strokes. It's really impressive.

Third, you simply typedef it with the const and non-const versions:

typedef BaseIterator<Value> iterator;
typedef BaseIterator<const Value> const_iterator;

The library explictly show you how to make the const_iterator version be constructible from the iterator version.

Fourth, for the reverse thing, there is a special reverse_iterator object, that is built on a regular iterator and move backwards :)

All in all, a really elegant and yet fully functional way of defining iterators on custom classes.

I regularly write my own container adaptors, and it's less about DRY than simply saving myself some typing!

Matthieu M.
+1 for boost::iterator_adaptor. I use it too and it work great for me.
n1ck