Re: Weird behaviour from std::list<>::reverse_iterator
On Aug 25, 12:46 pm, Rune Allnor <all...@tele.ntnu.no> wrote:
On 25 Aug, 11:27, James Kanze <james.ka...@gmail.com> wrote:
On Aug 24, 7:53 pm, Rune Allnor <all...@tele.ntnu.no> wrote:
On 24 Aug, 19:34, Victor Bazarov <v.Abaza...@comAcast.net> wrote:
The semantics is clear: I have used reverse_iterators to work
my way from list.rbegin() to some item in the list. The
semantically simple call
list.erase(middle);
spawns a compiler error.
That's because there's no function list<>::erase which takes
a reverse iterator.
Your misconception is that *middle == *(middle.base()).
It isn't. The reverse iterator's 'base' iterator points
to a different value in the container. At least IME.
That's the opinion of the standard, too. In fact,
reverse_iterator<>::base is guaranteed to point to the
element after (in the original sequence) the one pointed to
by the reverse iterator. The reason for this is simple:
I can see the technical arguments why things are done like
this, but I can't see why these technicalities are exposed to
the users of the STL.
They're more than just technicalities. What should
list<>::rend().base() return? (Remember that the iterator
returned from list<>::rend() doesn't have access to the list
itself.) The standard generally tries to avoid specifying
something that is impossible to implement.
Whenever I, as *user* of the STL, uses a reverse iterator to
search for an item in a container, the natural semantics of
iterators (as a general concept, not C++ type) is to
manipulate the element in the container referenced by that
iterator. Not the element before. Not the element after.
So from a semantic point of view, the obvious thing to try is
std::list<T> list;
std::list<T>::reverse_iterator i = ...;
list.erase(i);
and then expect the item referenced by i to be erased.
That, on the other hand, could be done. As far as I know, no
one has suggested it, but it would certainly be possible to
overload list<>::erase (and the erase functions in the other
containers) to take a reverse iterator, and that these
overloaded functions encapsulate the necessary logic.
Isn't that the philosophy behind the STL?
Not really. The philosophy is generally let the user beware,
with undefined behavior lurking at every (mis)step.
That users should only worry about semantics and not need to
know about implementation details?
I'm game, but first you'll have to propose an implemenation that
works. Ignoring implementation details supposes that there is
an implementation, which supposes that an implementation is
possible.
Note too that there are contexts where returning an iterator one
beyond is exactly what is wanted:
std::string
rightTrim(
std::string const& s,
SetOfCharacter const&
toRemove )
{
return std::string(
s.begin(),
std::find_if(
s.rbegin(), s.rend(), std::not1
( toRemove.contains() ) )
.base() ) ;
}
(FWIW: SetOfCharacter::contains() returns a predicate object,
which evaluates to true if the set contains the character in
question.)
If you're iterating backwards, you're often looking for a new
end to the sequence. So you find the last element in the new
sequence, but the end iterator defining the new sequence should
be one past this end.
--
James Kanze (GABI Software) email:james.kanze@gmail.com
Conseils en informatique orient=E9e objet/
Beratung in objektorientierter Datenverarbeitung
9 place S=E9mard, 78210 St.-Cyr-l'=C9cole, France, +33 (0)1 30 23 00 34