Search code examples
c++iterator

Can I convert a reverse iterator to a forward 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?


Solution

  • 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.