I have the following code:
int main()
{
vector<int> v;
for(int i = 0; i < 10; ++i)
v.push_back(i);
auto it = v.begin() + 3;
cout << "Iterator: " << *it << endl;
vector<int>::reverse_iterator revIt(it);
cout << "Reverse iterator: " << *revIt << endl;
}
After running this code I get the following output:
Iterator: 3
Reverse iterator: 2
Could someone explain why the 2 values differ ?
Reverse iterators 'correspond' to a base iterator with an offset of one element because of how rbegin() and rend() have to be represented using base iterators that are valid (end() and begin() respectively). For example, rend() cannot be represented by an interator that 'points' before the container's begin() iterator, although that's what it logically represents. So rend()'s 'base iterator' is begin(). Therefore, rbegin()'s base iterator becomes end().
A reverse iterator automatically adjusts for that offset when it is dereferenced (using the * or -> operators).
An old article by Scott Meyers explains the relationship in detail along with a nice picture:
Guideline 3: Understand How to Use a reverse_iterator’s Base iterator
Invoking the base member function on a reverse_iterator yields the
“corresponding” iterator, but it’s not really clear what that means.
As an example, take a look at this code, which puts the numbers 1-5 in
a vector, sets a reverse_iterator to point to the 3, and sets an
iterator to the reverse_iterator’s base:
vector<int> v;
// put 1-5 in the vector
for (int i = 1; i <= 5; ++i) {
v.push_back(i);
}
// make ri point to the 3
vector<int>::reverse_iterator ri =
find(v.rbegin(), v.rend(), 3);
// make i the same as ri's base
vector<int>::iterator i(ri.base());
After executing this code, things can be thought of as looking like
this:
This picture is nice, displaying the characteristic offset of a
reverse_iterator and its corresponding base iterator that mimics the
offset of rbegin() and rend() with respect to begin() and end(), but
it doesn’t tell you everything you need to know. In particular, it
doesn’t explain how to use i to perform operations you’d like to
perform on ri.
Looks like the documentation says they do that to handle past-the-end elements, i.e. if you reverse an iterator that is past the end, the new reverse iterator points to the last element.
The first paragraph of 24.5.1 Reverse iterators says:
Class template reverse_iterator is an iterator adaptor that iterates from the end of the sequence defined by its underlying iterator to the beginning of that sequence. The fundamental relation between a reverse iterator and its corresponding iterator i is established by the identity:
&*(reverse_iterator(i)) == &*(i - 1).
The value returned by rend() cannot point before begin(), because that is not valid. So it was decided that rend() should contain the value of begin() and all other reverse iterators be shifted one position further. The operator* compensates for this and accesses the correct element anyway.
Reverse iterator looks always "one before" then the forward, since its range is shifted by one:
Forward iterator goes from begin() (the first element) to end() (past the last: [begin-end) is opened at the end side)
Reverse iterator goes from rbegin() { return reverse_iterator(end()); } to rend() { return reverse_iterator(begin()); } by definition, but also has to walk the open range [rbegin-rend) having rbegin to be the last (not "past the last") and rend to be "before the first" (not "the first") hence a 1 difference to be accommodated.
Related
Following lines of C++ code gives runtime error but if erase operation mymap.erase(v) is removed it works:
map<int,int> mymap = {{1,0},{2,1},{9,2},{10,3},{11,4}};
for(auto it=mymap.rbegin();it!=mymap.rend();){
int v=it->first;
++it;
mymap.erase(v);
}
demo
Here iterator it is changed before deleting its value v, so iterator it should remain unaffected I believe.
When you are calling erase(v), you are invalidating the base iterator that the next reverse_iterator (from ++it) is using. So you need to create a new reverse_iterator from the base iterator that precedes the erased value.
Also, rather than erasing the value that the reverse_iterator is referring to, you should erase the base iterator instead, since you already know which element you want to erase. There is no need to make the map go hunting for the value again.
This works for me:
map<int,int> mymap = {{1,0},{2,1},{9,2},{10,3},{11,4}};
for(auto it = mymap.rbegin(); it != mymap.rend(); ){
auto v = --(it.base());
v = mymap.erase(v);
it = map<int,int>::reverse_iterator(v);
}
Demo
On the other hand, this loop is essentially just erase()'ing all elements from mymap, so a better option is to use mymap.clear() instead.
Indeed, std::map::erase:
References and iterators to the erased elements are invalidated. Other references and iterators are not affected.
But std::reverse_iterator
For a reverse iterator r constructed from an iterator i, the relationship &*r == &*(i-1) is always true (as long as r is dereferenceable); thus a reverse iterator constructed from a one-past-the-end iterator dereferences to the last element in a sequence.
Perhaps it gets more clear when you look at the image on the cppreference page.
The crucial part is "Reverse iterator stores an iterator to the next element than the one it actually refers to".
As a consequence (small modification to your code)
auto& element = *it; // fails in the next iteration, because...
int v = element.first;
++it; // now it stores an iterator to element
mymap.erase(v); // iterators to element are invalidated
You are erasing the element that is used by it in the next iteration.
I have a question in iterators about the difference between begin() and rend().
#include <iostream>
#include <array>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{
vector<int> v1;
v1 = {9,2,6,4,5};
cout<<*v1.begin();
cout<<*v1.rend();
return 0;
}
cout<<*v1.begin();
returns 9
but
cout<<*v1.rend();
returns a number that is not 9
Why are there such different results?
In C++, ranges are marked by a pair of iterators marking the beginning of the range and a position one past the end of the range. For containers, the begin() and end() member functions provide you with a pair of iterators to the first and past-the-end positions. It’s not safe to read from end(), since it doesn’t point to an actual element.
Similarly, the rbegin() and rend() member functions return reverse iterators that point, respectively, to the last and just-before-the-first positions. For the same reason that it’s not safe to dereference the end() iterator (it’s past the end of the range), you shouldn’t dereference the rend() iterator, since it doesn’t point to an element within the container.
Dereferencing end() or rend() has undefined behaviour.
begin() points to the first element, rbegin() points to the last element. end() (in most cases) points to one after the last element and rend() effectively points to one before the first element (though it isn't implemented like that).
What is the difference between begin () and rend ()?
begin returns an iterator to the first element of the container.
rend returns a reverse iterator to one before the first element of the container (which is one past the last element in the reverse iterator range).
*v1.rend()
The behaviour of indirecting through the rend iterator is undefined (same goes for indirecting through end iterator).
Why are there such different results?
Besides behaviour being undefined in one case and not the other, since they refer to different to different elements (one of them being an element that doesn't exist), there is little reason to assume the results to be the same.
vector::rend() is a built-in function in the C++ standard library which returns a reverse iterator pointing to the theoretical element right before the first element in the array container. but vector::begin() returns an iterator pointing to the first element in the vector.
See this code :
for (auto it = v1.rbegin(); it != v1.rend(); it++)
cout << *it << " ";
The vector elements in reverse order are :
5 4 6 2 9
To iterate in the vector always choose one of these methods together :
vector::begin() and vector::end()
vector::cbegin() and vector::cend()
vector::crbegin() and vector::crend()
vector::rbegin() and vector::rend()
For more information see "C++ Vector Tutorial With Example" by Ankit Lathiya.
Try it online
Giving the below code (say it's named deque.cpp)
#include <cstdio>
#include <deque>
int main()
{
std::deque<int> d = {1, 2, 3};
for (auto it = d.rbegin(); it != d.rend();) {
printf("it: %d\n", *it);
++it;
d.pop_back();
}
return 0;
}
Compile with g++ -std=c++11 -o deque deque.cpp, it runs well:
$ ./deque
it: 3
it: 2
it: 1
But if compile with -D_GLIBCXX_DEBUG (g++ -std=c++11 -o deque_debug deque.cpp -D_GLIBCXX_DEBUG, it gets below error:
$ ./deque_debug
it: 3
/usr/include/c++/4.8/debug/safe_iterator.h:171:error: attempt to copy-
construct an iterator from a singular iterator.
...
It looks like the second loop's ++it is constructing from an singular iterator.
But I think after the first loop's ++it, the iterator points to 2, and pop_back() should not invalidate it. Then why the error occurs?
Note: I know the code could be re-write as below:
while (!d.empty()) {
auto it = d.rbegin();
printf("it: %d\n", *it);
d.pop_back();
}
And the error will be gone.
But I do want to know what exactly happens on the error code. (Does it mean the reverse iterator dose not actually point to the node I expect, but the one after it?)
Update: #Barry's answer resolved the question.
Please let me put an extra related question: the code
for (auto it = d.rbegin(); it != d.rend();) {
printf("it: %d\n", *it);
d.pop_back();
++it; // <== moved below pop_back()
}
is expected to be wrong, where ++it should be operating on an invalidated iterator. But why the code does not cause error?
The problem here arises from what reverse iterators actually are. The relevant relationship for a reverse iterator is:
For a reverse iterator r constructed from an iterator i, the relationship &*r == &*(i-1) is always true (as long as r is dereferenceable); thus a reverse iterator constructed from a one-past-the-end iterator dereferences to the last element in a sequence.
When we then do std::deque::pop_back(), we invalidate:
Iterators and references to the erased element are invalidated. The past-the-end iterator is also invalidated. Other references and iterators are not affected.
rbegin() is constructed from end(). After we increment it the first time, it will dereference to the 2 but its underlying base iterator is pointing to the 3 - that's the erased element. So the iterators referring to it include your now-advanced reverse iterator. That's why it's invalidated and that's why you're seeing the error that you're seeing.
Reverse iterators are complicated.
Instead of incrementing it, you could reassign it to rbegin():
for (auto it = d.rbegin(); it != d.rend();) {
printf("it: %d\n", *it);
d.pop_back();
// 'it' and 'it+1' are both invalidated
it = d.rbegin();
}
Erasing from the underlying container invalidates an iterator. Quoting from the rules:
Iterators are not dereferenceable if
they are past-the-end iterators
(including pointers past the end of an array) or before-begin
iterators. Such iterators may be dereferenceable in a particular
implementation, but the library never assumes that they are.
they are singular iterators, that is, iterators that are not associated with any sequence. A null pointer, as well as a default-constructed pointer
(holding an indeterminate value) is singular
they were invalidated by
one of the iterator-invalidating operations on the sequence to which
they refer.
Your code causes the iterator to be invalidated by the pop_back operation, and thus as per the third point above, it becomes non dereferenceable.
In your while loop you're avoiding this problem by getting a (new) valid iterator in each loop repetition.
am beginner to c++ and i want to know how operator ++ moves iterator backward. As i know iterator.begin() and iterator.end() returns pointer to fist index and last index respectively.
vector<int>::iterator it = myvector.begin();
when we do it++ it will move to next index. this is clear to me but i am completely confused with reverse iterator.
vector<int>::reverse_iterator rit = myvector.rbegin();
when we do rit++ it will move to backward. I want know how this is implemented in case of reverse iterator. Is it operator overloading or something which i don't know. Please give me right way to understand these things.
please give more detail knowledge.
_***************_
^begin ++---> ^end
^rend <---++ ^rbegin
As i know iterator.begin() and iterator.end() returns pointer to fist index and last index respectively.
Close, but not necessarily:
they return iterators, not pointers....
end() returns an iterator that's notionally "past" the last valid index.
Internally the vector iterators normally do store pointers though, and a ++ or -- on the iterator goes through a forwarding overloaded operator to perform a corresponding ++ or -- on the pointer.
For reverse iterators, the overloaded operator++ simply performs a -- on the pointer, and vice versa. For example, the Visual C++ library on my computer does this in class _Revranit, from which reverse_iterator is derived:
_Myt& operator++()
{
--current;
return (*this);
}
_Myt operator++(int)
{
_Myt _Tmp = *this;
--current;
return (_Tmp);
}
When you do it++ or rit++ they don't really mean "move forward or backward in the collection", they both mean "move to the next element you want to iterate over".
If you use begin(), you're saying "I want to iterate forwards", so it++ moves forwards.
If you use rbegin(), you're saying "I want to iterate backwards", so rit++ moves backwards.
Yes, this is operator overloading. When you do rit++, the compiler internally translates that do
rit.operator++(0);
Here, operator++ is a method of the reverse iterator class that knows how to move the iterator. The dummy integer argument is needed to differentiate between prefix and postfix operator++.
Yes, there's a reverse_iterator::operator++ overload which advances "backwards" from the last element to one-before-the-beginning.
The behavior is equivalent to (the page on cppreference might contain another possible implementation)
reverse_iterator operator++(int)
{
reverse_iterator __tmp(*this);
--current; // This is an internal iterator
return __tmp;
}
Notice that how this is exactly accomplished might vary from implementation to implementation. As far as you comply with the standard requirements, you have some degrees of freedom.
The relationship between a reverse iterator r and the iterator i it is constructed from is:
&*r == &*(i-1)
If end is the one-past-the-end element in a sequence, then the first element in the reversed sequence points to *(end – 1 )
We (all) know, erasing an element, pointer by an iterator invalidates the iterator, for example:
std::map< .. > map_;
std::map< .. >::iterator iter;
// ..
map_.erase( iter ); // this will invalidate `iter`.
But, what about:
map_.erase( map_.begin() );
is this safe? Will map_.begin() be a valid iterator, pointing to the (new) first element of the map?
"test it" is not a solution.
begin() is not an iterator but returns an iterator. After erasing the first element, begin()returns another (valid) iterator.
std::map<int, int> m;
m[1] = 2;
m[2] = 3;
m.erase(m.begin()); // <- begin() points to 1:2
std::cout << m.begin()->second; // <- begin() points to 2:3 now
On cppreference, we see:
All iterators (pos, first, last) must be valid and dereferenceable,
that is, the end() iterator (which is valid, but is not
dereferencable) cannot be used.
That pretty much answers your question. As long as the iterator returned by begin() is valid and dereferencable, it is OK to be used in std::map::erase(). A good way then to check if begin() is OK to be used in std::map::erase is by checking it if it is not equal to end():
if(map.begin() != map.end()) {
map.erase(map.begin());
}
Alternatively, you could also check if the map is empty, and use std::map::erase if it isn't
if(!map.empty()) {
map.erase(map.begin());
}
Of course it is.
map::begin returns a valid iterator referring to the first element in the map container.
http://www.cplusplus.com/reference/map/map/begin/
Pay attention to empty map.
is this safe?
Yes. It invalidates the temporary iterator returned by this call to begin(), and that iterator is destroyed at the end of the statement.
Will map_.begin() be a valid iterator, pointing to the (new) first element of the map?
Yes, unless the map is now empty. Erasing an element does not prevent you from creating new iterators to the remaining elements; that would make the map unusable.