Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I convert a reverse iterator to a forward iterator?

Tags:

c++

iterator

I have a class called Action, which is essentially a wrapper around a deque of Move objects.

Because I need to traverse the deque of Moves both forward and backwards, I have a forward iterator and a reverse_iterator as member variables of the class. The reason for this is becuase I need to know when I have gone one past the "end" of the deque, both when I am going forwards or backwards.

The class looks like this:

class Action { public:     SetMoves(std::deque<Move> & dmoves) { _moves = dmoves; }     void Advance();     bool Finished()      {         if( bForward )             return (currentfwd==_moves.end());         else             return (currentbck==_moves.rend());     } private:     std::deque<Move> _moves;     std::deque<Move>::const_iterator currentfwd;     std::deque<Move>::const_reverse_iterator currentbck;     bool bForward; }; 

The Advance function is as follows:

void Action::Advance {     if( bForward)         currentfwd++;     else         currentbck++; } 

My problem is, I want to be able to retrieve an iterator to the current Move object, without needing to query whether I am going forwards or backwards. This means one function returning one type of iterator, but I have two types.

Should I forget returning an iterator, and return a const reference to a Move object instead?

like image 208
BeeBand Avatar asked Jan 10 '10 17:01

BeeBand


People also ask

How does a reverse iterator work?

Returns a reverse iterator pointing to the last element in the vector (i.e., its reverse beginning). Reverse iterators iterate backwards: increasing them moves them towards the beginning of the container. rbegin points to the element right before the one that would be pointed to by member end.

What is forward iterator?

Forward Iterator is a combination of Bidirectional and Random Access iterator. Therefore, we can say that the forward iterator can be used to read and write to a container. Forward iterators are used to read the contents from the beginning to the end of a container.


2 Answers

Reverse iterators have a member base() which returns a corresponding forward iterator. Beware that this isn't an iterator that refers to the same object - it actually refers to the next object in the sequence. This is so that rbegin() corresponds with end() and rend() corresponds with begin().

So if you want to return an iterator, then you would do something like

std::deque<Move>::const_iterator Current() const {     if (forward)         return currentfwd;     else         return (currentbck+1).base(); } 

I would prefer to return a reference, though, and encapsulate all the iteration details inside the class.

like image 159
Mike Seymour Avatar answered Sep 23 '22 16:09

Mike Seymour


This is exactly the sort of problem that prompted the design of STL to start with. There are real reasons for:

  1. Not storing iterators along with containers
  2. Using algorithms that accept arbitrary iterators
  3. Having algorithms evaluate an entire range instead of a single item at a time

I suspect what you're seeing right now is more or less the tip of the iceberg of the real problems. My advice would be to take a step back, and instead of asking about how to deal with the details of the design as it currently stands, ask a somewhat more general question about what you're trying to accomplish, and how best to accomplish that end result.

For those who care primarily about the question in the title, the answer is a heavily qualified "yes". In particular, a reverse_iterator has a base() member to do that. The qualifications are somewhat problematic though.

The demonstrate the problem, consider code like this:

#include <iostream> #include <vector> #include <iterator>  int main() {      int i[] = { 1, 2, 3, 4};     std::vector<int> numbers(i, i+4);      std::cout << *numbers.rbegin() << "\n";     std::cout << *numbers.rbegin().base() << "\n";     std::cout << *(numbers.rbegin()+1).base() << "\n";      std::cout << *numbers.rend() << "\n";     std::cout << *numbers.rend().base() << "\n";     std::cout << *(numbers.rend()+1).base() << "\n"; } 

Running this at this particular moment on my particular machine produces the following output:

4 0 4 -1879048016 1 -1879048016 

Summary: with rbegin() we must add one before converting to a forward iterator to get an iterator that's valid -- but with rend() we must not add one before converting to get a valid iterator.

As long as you're using X.rbegin() and X.rend() as the parameters to a generic algorithm, that's fine--but experience indicates that converting to forward iterators often leads to problems.

In the end, however, for the body of the question (as opposed to the title), the answer is pretty much as given above: the problem stems from trying to create an object that combines the collection with a couple of iterators into that collection. Fix that problem, and the whole business with forward and reverse iterators becomes moot.

like image 39
Jerry Coffin Avatar answered Sep 19 '22 16:09

Jerry Coffin