Trying to develop an intuition with addressing vector elements with indexes through forward and backward iterators. Since, for example per en.cppreference.com,
“For a reverse iterator r constructed from an iterator i, the relationship &r == &(i - 1) is always true”
is it obvious that in order to keep it_backward
pointing to the same element as it_backward
I have to compensate this with, subtracting one from the resulting backward iterator:
std::vector i1{ 0,1,2,3,4 };
const int element_offset=3;
auto it_forward = i1.begin()+element_offset;
auto it_backward = std::make_reverse_iterator(it_forward)-1;
std::cout << std::endl << "*(it_forward) = " << *(it_forward);
std::cout << std::endl << "*(it_backward) = " << *(it_backward);
to be more precise it would be better to use:
auto it_backward = std::prev(std::make_reverse_iterator(it_forward));
At the same time when we just reverse the range, this is done transparently for programmer and we don’t need to adjust the iterators and I see why.
std::copy(std::make_reverse_iterator(i1.end()),
std::make_reverse_iterator(i1.begin()),
std::ostream_iterator<int>(std::cout, ", "));
My questions is, since these ways of addressing (ranges, elements, ranges limited by elements) sometimes used together, what is the best way to develop an intuition, when we have to adjust the backward iterator and when we don’t?
I can do this every time double checking myself and code, but maybe there is some kind of a good rule of thumb which could help to see when to adjust and when not to?
Pairs of iterators denote half-open ranges. The first iterator is dereferenceable until it is equal to the second.
If you required passing an iterator to the element you desired to std::reverse_iterator
, then you couldn't use end
for the start, nor could you use the begin
of an empty range. Also there is no iterator value that corresponds to the past-the-end of the reversed range.
What you should avoid thinking about is constructing a reverse_iterator
to a particular element, and instead construct pairs of reverse iterators that denote the range of elements you want, having swapped the begin and end. I.e. instead of your example
auto it_forward = i1.begin()+element_offset;
auto it_backward = std::make_reverse_iterator(it_forward)-1;
You would have
auto it_forward_begin = i1.begin()+element_offset;
auto it_forward_end = i1.begin()+element_offset+1;
auto it_backward_begin = std::make_reverse_iterator(it_forward_end);
auto it_backward_end = std::make_reverse_iterator(it_forward_begin);
Now you can simplify that, inlining the end iterator
auto it_backward = std::make_reverse_iterator(it_forward+1);
or
auto it_backward = std::make_reverse_iterator(std::next(it_forward));