+-- v.begin() +-- v.end()
| |
v v
+---+---+---+---+---+---+ - +
| o | o | o | o | o | o | x |
+---+---+---+---+---+---+ - +
+ - +---+---+---+---+---+---+
| x | o | o | o | o | o | o |
+ - +---+---+---+---+---+---+
^ ^
| |
+-- v.rend() +-- v.rbegin()
(ASCII copied, and edited, from this answer, which is actually prompted me to ask the present question.)
I do see the advantage of having that &*rit == &*(rit.base() - 1), because this way I can take the rit.base() for any reverse iterator rit and I'll always get a valid iterator.
But at the same time
- I cannot dereference
v.rbegin().base(); I have to remember subtracting 1 first,*(v.rbegin().base() - 1), - and I cannot dereference
v.rend().base() - 1exactly because I can't dereferencev.rend().
What if the design was that &*rit == &*rit.base()?
- We couldn't call
v.rend().base(), true, but that simply corresponds to not being able to dereferencev.rend().base() - 1in the current design; - We wouldn't be able to get
v.end()directly from a reverse iterator, not even the closest one,b.rbegin(), but that simply to the- 1we have to add torit.base()in the current design to get a forward iterator to the same element as the reverse.
What I mean is that it seems to me that whether the design decision was that &*rit == &*(rit.base() - 1) (as it was) or that &*rit == &*rit.base(), we'd have had the same amount of convenience
rit.base()is always ok in the actual design,- 1is not generally needed in the alternative design
and inconvenience
- can't dereference all valid
rit.base()s in the actual design, - need to
+1v.rbegin()to getv.end()in the alternative design,
just in opposite situations.
So my question is: is there a definitive advantage in making the choice that has indeed been made? Or was it just a flipped coin?
I see that there's no valid iterator before the first element, but that's my point. When reverse iterators were introduced, what would have been wrong with making std::prev(v.begin()) a valid, non-dereferenceable iterator just like v.end()?
In hindsight, I do see one unquestionable advantage.
I didn't see the advantage of v.rbegin().base() == v.end()/v.rend().base() == v.begin() because why would I want to create v.end()/v.begin() from their reverse counterparts?
But if I have two reverse iterators rit1 and rit2 that define the range (rit1, rit2], then taking it1 = rit1.base() and it2 = rit2.base() allows to refer easily to the same range of elements in the opposite direction, [it1, it2).
Long story short: _I should have read §9.4.1 from The C++ Standarl Library - 2nd ed. first.