0

The reverse iterator of a std::map.rbegin() returns the position of the element which contains the largest key_type in a std::map.
The element with the largest Key can be accessed using the position returned by map.rbegin() like this:
map.rbegin()->first

However the position returned by map.rbegin() cannot be used to erase this element.

Why can I access but not erase the same position ?

std::map <unsigned int, std::string> map;

map.emplace(5, "aaa");
map.emplace(7, "bbb");
map.emplace(3, "ccc");

cout << map.rbegin()->first << " : " << map.rbegin()->second << endl;       //Reverse iterator works for accessing the element

map.erase(map.rbegin());      //Erase the largest element in the map - Error C2664 converting argument 1 from 'std::reverse_iterator<std::_Tree_iterator<std::_Tree_val<std::_Tree_simple_types<_Ty>>>>' to 'std::_Tree_iterator<std::_Tree_val<std::_Tree_simple_types<_Ty>>>'    
map.erase(std::prev(map.end()));    //Erase the largest element in the map - WORKS
map.erase(std::prev(map.rbegin().base()));  //Erase the largest element in the map - WORKS

It seems logical to me that if a function/method needs an iterator to obtain only one position, then it should not matter what kind of iterator it is.
Things would be different if that iterator described something else than just one position (e.g. direction, too...)

NOTE: I did not ask "How to call erase with a reverse iterator?", I provided a solution to this operation in the last line of my code.
I am asking why I cannot erase with a reverse iterator. i.e. why doesn't the STL simply cast the std::map::reverse_iterator to std::map:iterator if it only needs to use it to obtain the position of one element to be erased.

George Robinson
  • 1,500
  • 9
  • 21
  • Simply put, `std::map::erase` does not have an overload for reverse iterators. It accepts `map::iterator`, `map::const_iterator` and `map::key_type`. None of these are a reverse iterator type. – François Andrieux Dec 07 '20 at 14:19
  • 1
    A "mix and match" interface would be very prone to errors, and it would complicate the library immensely if every function would need overloading on reverse and "normal" iterators. (A function with four iterator parameters would possibly need sixteen overloads, and if you didn't provide all of them, at least one person would be angry that their particular desired combination wasn't included.) – molbdnilo Dec 07 '20 at 14:19
  • 1
    It doesn't really solve your problem, but reverse iterators have a `base` function that returns a forward iterator + 1. `(map.rbegin().base() == map.end())` – super Dec 07 '20 at 14:25
  • @super TIL. Incredibly useful function, thanks. – François Andrieux Dec 07 '20 at 14:30
  • I see you've edited your question to link to the "how" target, so I've added a "why" target as well. Unfortunately, the "why" target has been closed as a dupe of the "how" target, and the existing answer on the "why" target doesn't really answer the question. – cigien Dec 07 '20 at 14:35
  • Thanks for editing again to clarify the question. I'll take another look at whether the targets are appropriate. – cigien Dec 07 '20 at 14:55
  • Ok, I've reopened the question. – cigien Dec 07 '20 at 15:14
  • Ultimately, if the iterator is only needed to obtain the position it should not matter what kind of iterator carries that information...and we should be able to convert iterators to each other when their directional information is superfluous. – George Robinson Dec 07 '20 at 20:56

1 Answers1

0

Why is the reverse iterator .rbegin() prevented from being used as a position of the elemet to be erased?

Because the type of the parameter of erase is not reverse_iterator, but iterator. Those are different types.

Why can I access but not erase the same position?

Knowing the location of the object in memory (which is what you know when you can access an object) is not generally sufficient to access the data structure that contains the element.

eerorika
  • 232,697
  • 12
  • 197
  • 326
  • But in this case, this iterator is quite sufficient for accessing the data structure that contains this element. This is evidenced by the successful execution of `cout << map.rbegin()->first` – George Robinson Dec 16 '20 at 11:49
  • @GeorgeRobinson That's true. On the other hand, `std::pair*, unsigned>` is also sufficient for accessing the data structure. Yet, for some reason you cannot pass it to `erase`. The reason is the same why you cannot pass a `reverse_iterator`: Because the type of the argument is `iterator` which is neither that pair, nor `reverse_iterator`. – eerorika Dec 16 '20 at 11:53
  • Yes, the type of parameter of the `erase()` is different, but the real question is why no overload is provided for reverse iterators when they contain ALL the information needed to access the data structure. Also the code of `std::map` implements both type of iterators, but it does not implement `std::pair`. – George Robinson Dec 16 '20 at 11:54
  • @GeorgeRobinson I cannot speak for Stepanov. I don't know if rationale for such choices is documented anywhere. – eerorika Dec 16 '20 at 12:02