Why we need rbegin and rend? - c++

Since we now have advance() and the prev() to move iterator to go front or go back, and we already have begin() and end().
I wonder is there any situation we better/have to move reverse iterator back and front?

Algorithms often take two iterators that specify a range of elements. For example std::for_each:
std::vector<int> x;
std::for_each(x.begin(),x.end(),foo);
If you want to make for_each iterate in reverse order (note: for_each does iterate in order) then neither advance nor prev are of any help, but you can use reverse iterators:
std::for_each(x.rbegin(),x.rend(),foo);

Because using begin() and end() to iterate in reverse looks horrible:
std::vector<int> v {1, 2, 3};
if(!v.empty()) { //need to make sure of that before we decrement
for(auto it = std::prev(v.end()); ; --it) {
//do something with it
if(it == v.begin()) {
break;
}
}
}
Compare it with reverse iterator version:
std::vector<int> v {1, 2, 3};
for(auto it = v.rbegin(); it != v.rend(); it++) {
//do something with it
}

When you have a function template that takes iterators, and want it to operate on the data in reverse.
E.g.
std::string s = "Hello";
std::string r(s.rbegin(), s.rend());
std::cout << r;

When you use algorithms like std::for_each(), std::accumulate(), std::find_if()... they systematically progress with ++.
If you want this progression to physically occur backwards, then the reverse
iterators are useful.

I guess it is good practise because it seems odd if you start from end and finish in begin. You can easily say last but one by using rbegin.
vector::reverse_iterator itr1;
for (itr1 = vec.rbegin(); itr1 < vec.rend(); itr1++) {
if (*itr1 == num) {
vec.erase((itr1 + 1).base());
}
}
You can use as a function which deletes that Which num want to erase in vector

The need for rbegin()/rend() is because begin() is not the same as rend(), and end() is not rbegin(), see this image from cppreference
This way, you can use any algorithm going forward from beginning to end or backwards from the last to the first element.

There are examples with for each. However, more general, it allows you to reuse any algorithm or operators that works with iterators with advancing, to do the same thing but in a reverse order.

Related

Erasing many vector elements while going through it with 'auto'

Let's say that I have vector of pairs, where each pair corresponds to indexes (row and column) of certain matrix I am working on
using namespace std;
vector<pair<int, int>> vec;
I wanted to, using auto, go through the whole vector and delete at once all the pairs that fulfill certain conditions, for example something like
for (auto& x : vec) {
if (x.first == x.second) {
vec.erase(x);
}
}
but it doesn't work, as I suppose vec.erase() should have an iterator as an argument and x is actually a pair that is an element of vector vec, not iterator. I tried to modify it in few ways, but I am not sure how going through container elements with auto exactly works and how can I fix this.
Can I easily modify the code above to make it work and to erase multiple elements of vector, while going through it with auto? Or I should modify my approach?
For now it's just a vector of pairs, but it will be much worse later on, so I would like to use auto for simplicity.
vector::erase() invalidates any outstanding iterators, including the one your range based for loop is using. Use std::remove_if():
vec.erase(
std::remove_if(
vec.begin(),
vec.end(),
[](const pair<int,int> &xx) { return xx.first == xx.second; }
), vec.end()
);
std::remove_if() swaps the elements to the end of the vector and then you can safely erase them.
I would prefer something like this:
pair<int, int> pair = nullptr;
auto iter = vec.begin();
while(iter != vec.end()){
pair = (*iter);
if(pair.first == pair.second){
iter = this->vec.erase(iter);
}else{
++iter;
}
}

c++ std::list compile issue? related to list.end() and list.end() - 1

In below code.
int main() {
list<int> m_list;
m_list.push_back(1);
list<int>::iterator it1 = (--m_list.end()); // it works, *it1 return 1;
list<int>::iterator it2 = (m_list.end() - 1); // compile issue?
}
Anybody explain why in list (m_list.end() - 1) has compile issue? and why (--m_list.end()) is OK?
If we change to others, vector, string. both cases do work.
int main() {
vector<int> m_vector;
m_vector.push_back(1);
vector<int>::iterator it1 = (--m_vector.end()); // both work
vector<int>::iterator it2 = (m_vector.end() - 1); // both work
}
The reason behind this is that list::end() returns a bidirectional iterator which does not support such operation.
Source:
http://www.cplusplus.com/reference/iterator/BidirectionalIterator/
On the other hand, vector::end() and string::end() returns a random access iterator which supports such operation.
http://www.cplusplus.com/reference/iterator/RandomAccessIterator/
Edit:
If you really want to accomplish the task, use std::prev() function
list<int>::iterator it2 = (std::prev(m_list.end(), 1));
As suggested by Pete Becker, "The second argument to std::prev has a default of 1"
So, you may do this also:
list<int>::iterator it2 = (std::prev(m_list.end()));
Anybody explain why in list (m_list.end() - 1) has compile issue?
Because list iterator doesn't support random access. Only random access iterators are guaranteed to support operator- (and operator+).
and why (--m_list.end()) is OK?
Because bidirectional iterators support operator-- (and operator++). List iterator is bidirectional.
If we change to others, vector, string. both cases do work.
Both vector and string have random access iterators.

Why no operator+ for std::list iterators?

I was about to write code like this:
std::list<whatevertype> mylist;
// ...
std::list<whatevertype>::iterator it;
for(it = mylist.begin(); it != mylist.end(); ++it) {
// ...
if(some condition)
mylist.erase(it);
}
But I realized, this code is wrong: mylist.erase(x) will invalidate the iterator it, so the ++it is likely to fail.
So I tried changing it to
std::list<whatevertype>::iterator it;
std::list<whatevertype>::iterator nextit;
for(it = mylist.begin(); it != mylist.end(); it = nextit) {
// ...
nextit = it + 1;
if(some condition)
mylist.erase(it);
}
But, to my surprise, this failed: evidently operator+ is not defined for std::list iterators.
I've since found this other question and learned that the standard idiom for deleting "out from under" an iterator is more like
for(it = mylist.begin(); it != mylist.end(); ) {
if(some condition)
it = mylist.erase(it);
else ++it;
}
I believe I could also get away with
for(it = mylist.begin(); it != mylist.end(); ) {
// ...
std::list<whatevertype>::iterator previt = it;
++it;
if(some condition)
mylist.erase(previt);
}
But my question is, is there a reason that operator+ is not defined for these iterators?
One rule they had with the std iterators and collection was to make expensive things verbose.
On a list iterator, it+50 takes O(50) time. On a vector iterator, it+50 takes O(1) time. So they implemented + on vector iterators (and other random access iterators) but not on list iterators (and other weaker iterators).
std::next and std::advance and std::prev can solve your problem easier:
auto previt = std::prev(it);
or
auto nextit = std::next(it);
these also take a count, but because they are an explicit function call it was decided that them being expensive is acceptable.
Among other things, you can search for calls to std::next and std::prev and get iterator manipulation; + is heavily overloaded and finding the expensive calls is hard.
Note that std::basic_string doesn't follow the same conventions as other std containers.
It isn't that + is missing for all iterators. It is missing for std::list iterators.
That's because a list iterator is incredibly inefficient at random access. Therefore, making random access easy is a bad idea.
You can use std::advance. It makes it more evident that you are moving across the list one element at a time.
std::list uses a BidirectionalIterator which only defines increment and decrement. As std::list is a linked list the implementation of the iterator can only move one node at a time.
The interface is designed to make sure you know that moving by more than one element isn't a simple operation like it is with other iterators like a RandomAccessIterator returned from a std::vector.
see http://en.cppreference.com/w/cpp/concept/Iterator for a definition of the different iterator types.

Peeking the next element in STL container

Is it possible to peek next element in a container which the iterator currently points to without changing the iterator?
For example in std::set,
int myArray[]= {1,2,3,4};
set <int> mySet(myArray, myArray+4);
set <int>::iterator iter = mySet.begin();
//peek the next element in set without changing iterator.
mySet.erase(iter); //erase the element if next element is n+1
C++0x adds a handy utility function, std::next, that copies an iterator, advances it, and returns the advanced iterator. You can easily write your own std::next implementation:
#include <iterator>
template <typename ForwardIt>
ForwardIt next(ForwardIt it,
typename std::iterator_traits<ForwardIt>::difference_type n = 1)
{
std::advance(it, n);
return it;
}
You can use this in your example like so:
if (iter != mySet.end() && next(iter) != mySet.end() && *next(iter) == *iter + 1)
mySet.erase(iter);
Not with iterators in general. An iterator isn't guaranteed to be able to operate non-destructively. The classic example is an Input Iterator that actually represents an underlying input stream.
There's something that works for this kind of iterator, though. A Forward Iterator doesn't invalidate previous copies of itself by the act of moving forward through the collection. Most iterators (including those for STL collections) are at least Forward Iterators, if not a more functional version- only Input Iterators or Output Iterators are more restricted. So you can simply make a copy of your iterator, increment the copy and check that, then go back to your original iterator.
So your peek code:
set <int>::iterator dupe = iter;
++dupe;
// (do stuff with dupe)
set <int>::iterator iter2 = iter;
++iter2;
int peekedValue = *iter2;
You can always make a copy of the iterator and advance the copy:
set <int>::iterator iter = mySet.begin();
set <int>::iterator iterCopy = iter;
iterCopy++;
if (*iterCopy == something)
mySet.erase(iter);
But beware that iterCopy may no longer be valid once you erase iter.
for sequence containers (vector, deque, and list) you can call front which will give you a peek (more info on the lower part of this link).
This will not work for std::set as its nature does not allow for the [] operator, but for containers that do, you can do:
std::vector<int> v;
v.push_back(3);
v.push_back(4);
std::vector<int>::iterator it = v.begin();
std::cout << v[it - v.begin() + 1];
But this could be dangerous if it points to the last element in the container; but the same applies to the solution above. E.g. you'll have to make checks in both cases.

How do you insert with a reverse_iterator

I want to insert something into a STL list in C++, but I only have a reverse iterator. What is the usual way to accomplish this?
This works: (of course it does)
std::list<int> l;
std::list<int>::iterator forward = l.begin();
l.insert(forward, 5);
This doesn't work: (what should I do instead?)
std::list<int> l;
std::list<int>::reverse_iterator reverse = l.rbegin();
l.insert(reverse, 10);
l.insert(reverse.base(), 10); will insert '10' at the end, given your definition of the 'reverse' iterator. Actually, l.rbegin().base() == l.end().
Essentially, you don't. See 19.2.5 in TCPPPL.
The reverse_iterator has a member called base() which will return a "regular" iterator. So the following code would work in your example:
l.insert(reverse.base(), 10);
Be careful though because the base() method returns the element one after the orginal reverse_iterator had pointed to. (This is so that reverse_iterators pointing at rbegin() and rend() work correctly.)
Just in case it is helpful, as this it the first hit on a search, here is a working example of using rbegin. This should be faster than using std::stringstream or sprint. I defiantly call a member fmt_currency many thousands of times in some print jobs. The std::isalnum is for handling a minus sign.
std::wstring fmt_long(long val) {//for now, no options? Just insert commas
std::wstring str(std::to_wstring(val));
std::size_t pos{ 0 };
for (auto r = rbegin(str) + 1; r != str.rend() && std::isalnum(*r); ++r) {
if (!(++pos % 3)) {
r = std::make_reverse_iterator(str.insert(r.base(), L','));
}
}
return str;
}