13

Well, I know why, it's because there isn't a conversion, but why isn't there a conversion? Why can forward iterators be turned to reverse iterators but not the other way round? And more importantly, what can I do if I want to do this? Is there some adapter that allows you to iterate backwards using a forward iterator?

std::vector<int> buffer(10);
std::vector<int>::iterator forward = buffer.begin();
std::vector<int>::reverse_iterator backward = buffer.rbegin();
++forward;
++backward;
std::vector<int>::iterator forwardFromBackward = std::vector<int>::iterator(backward); // error! Can't convert from reverse_iterator to iterator!
std::vector<int>::reverse_iterator backwardFromForward = std::vector<int>::reverse_iterator(forward); // this is fine
Ben Hymers
  • 25,586
  • 16
  • 59
  • 84
  • 15
    There is a conversion: `backward.base();` – Yakov Galka Dec 10 '10 at 10:42
  • I'm pretty sure you can use `reverse_iterator` where needed by passing iterator type as template argument wherever you use `iterator`. Doesn't it solve your problem? – Grozz Dec 10 '10 at 10:45
  • 1
    You're absolutely right ybungalobill; my actual code is a bit more complex than this and I tried base() but it gave me errors so I gave up on it and my brain erased my knowledge of it. Thanks! If you add that exact text as an answer, I'll accept it, since really it *is* an answer. – Ben Hymers Dec 10 '10 at 11:00
  • 1
    @Ben: Then probably you should understand what errors you got when you used 'base()'. I'm still curious why isn't there a default conversion. – Yakov Galka Dec 10 '10 at 11:02
  • @Grozz, but the reverse_iterator contains the regular iterator as a member and you can extract it with base(). – CashCow Dec 10 '10 at 11:05
  • @ybungalobill You mean why it doesn't implicitly convert? Because implicit conversions are evil. With iterator->const_iterator it isn't really an implicit conversion, it is an implicit constructor which isn't quite as evil. An implicit constructor to iterator from reverse_iterator might have been possible. – CashCow Dec 10 '10 at 11:09
  • @ybungalobill, Turns out I couldn't understand the errors because it was a compiler bug in VC10, to do with auto, declarator lists and functions similar to base() (returning a templated type different to the templated class it's called on). I reported it and it's apparently fixed for VC11. – Ben Hymers Jan 07 '11 at 11:10
  • @ybungalobill That's not a conversion, it's the physical iterator. It is actually `(backward+1).base()`. Reason: for `reverse_iterator`, the physical location of a `reverse_iterator` is [_the element before_](http://stackoverflow.com/a/14760316/111307). – bobobobo Dec 10 '13 at 20:13

3 Answers3

19

You could write a helper function. One particularity of reverse_iterator is that base() gives a forward iterator that is next from the value that the reverse iterator dereferences to. This is because a reverse iterator physically points to the element after the one it logically points to. So to have the forward iterator to the same item as your reverse_iterator, you'll need to decrement the result of base() by one, or you could increment the reverse iterator first, then take the .base() of that.

Both examples are shown below:

#include <iostream>
#include <vector>
#include <iterator>

//result is undefined if passed container.rend()
template <class ReverseIterator>
typename ReverseIterator::iterator_type make_forward(ReverseIterator rit)
{
    return --(rit.base()); // move result of .base() back by one.
    // alternatively
    // return (++rit).base() ;
    // or
    // return (rit+1).base().
}

int main()
{
    std::vector<int> vec(1, 1);
    std::vector<int>::reverse_iterator rit = vec.rbegin();
    std::vector<int>::iterator fit = make_forward(rit);
    std::cout << *fit << ' ' << *rit << '\n';
} 

Warning: this behavior is different from that of the reverse_iterator(iterator) constructor.

Community
  • 1
  • 1
visitor
  • 8,564
  • 2
  • 26
  • 15
  • Looks like ybungalobill doesn't want the rep, so as the first answer to mention base() you win :) Thanks! – Ben Hymers Dec 10 '10 at 12:00
  • @Abyx: Yes, since you cannot decrement the temporary pointer returned from `rit.base()`, it might be better to create a named variable of `iterator_type` first. No specialization should be needed, though. – visitor Dec 10 '10 at 13:39
2

It's very common to have two (reverse) iterators span a range of values (such as in begin(),end() and rbegin(),rend()). For any range described by the two reverse iterators rA,rB, the range rB.base(),rA.base() will span the same range in the forward direction.

#include <iostream>
#include <iterator>
#include <vector>

int main() {
  std::vector<int> vec{10,11,12,13,14,15};

  // spans the range from 13 to 10
  auto rfirst=std::rbegin(vec)+2;
  auto rlast=std::rend(vec);

  // Loops forward, prints 10 11 12 13
  for(auto it = rlast.base(); it != rfirst.base(); ++it){
    std::cout << *it << " ";
  }
}

If conceptually you are only interested in a single item (such as the result of find_if), then use make_forward by @visitor. Even in this case, the range idea helps to keep track of the validity of the reverse iterator:

#include <iostream>
#include <iterator>
#include <vector>
#include <algorithm>

int main() {
  std::vector<int> vec{10,11,12,13,14,15};

  auto rfirst=std::rbegin(vec);
  auto rlast=std::rend(vec);

  auto rfound = std::find_if(rfirst,rlast, [](int v){ return v<13; });

  if(rfound != rlast){
    std::cout << *rfound << " "; // prints 12
    auto forwardFound = make_forward(rfound) ; 
    std::cout << *forwardFound << " "; // prints 12
  }
}
Johan Lundberg
  • 26,184
  • 12
  • 71
  • 97
0

You can get forward iterator from reverse iterator using this code

container.begin() + (reverseIter - container.rbegin() - 1);
Abyx
  • 12,345
  • 5
  • 44
  • 76
  • 3
    _Why_. Over a large list you are asking for lots of excessive traversal, and this is much messier than simply `(reverseIter+1).base()` – bobobobo Dec 10 '13 at 20:10
  • What list? it's a *vector*. – Abyx Dec 11 '13 at 12:06
  • If you're using iterators (as opposed to simple integer indexing), I assume the reason you're doing this is to be able to apply this formula to any `std::container` that uses iterators. `std::list` traversal is more expensive than `std::vector`, so you're much better off with traversing (just 1 element). – bobobobo Dec 11 '13 at 19:22
  • @bobobobo, meh and what if you're using a container which doesn't use `std::reverse_iterator`? – Abyx Dec 11 '13 at 19:32
  • 2
    Then the question is moot? – bobobobo Dec 12 '13 at 02:51