I am traversing an std::list using reverse iterators and erasing some elements from the list using their forward iterators that were obtained when inserting them. A sample program is shown below. I read that deleting elements from a list doesn't invalidate other iterators except for the ones referring to element that's deleted. But there's no mention of reverse_iterators and my program is crashing. Can someone please tell if the usage is incorrect?
What the program is doing is adding an element into the list, storing its iterator, reverse iterating the list and deleting the only element in the list using its stored iterator.
The output is pasted below the code sample.
#include <list>
#include <iostream>
using namespace std;
struct node
{
int data;
list<node*>::iterator iter;
} a;
int main()
{
list<node*> l;
a.data = 1;
l.push_front( &a );
a.iter = l.begin();
list<node*>::reverse_iterator ri = l.rbegin();
while ( ri != l.rend() )
{
cout << (*ri)->data << endl;
list<node*>::reverse_iterator rj = ri;
++ri;
if ( ri == l.rend() )
cout << "before erase: reached end" << endl;
l.erase((*rj)->iter);
if ( ri == l.rend() )
cout << "after erase : reached end" << endl;
else
cout << "after erase : Not reached end" << endl;
}
}
OUTPUT
1
before erase: reached end
after erase : Not reached end
610568524
before erase : reached end
Segmentation fault
Under VS2010 it will throw an exception here, on the first loop pass:
l.erase((*rj)->iter);
if ( ri == l.rend() ) // exception
That should give you a general idea what's going on. You see, reverse_iterator is just a wrapper for standard iterator. That said, you should remember that it's got base() member that returns underlying iterator - you don't have to store it elsewhere, like you do in node struct.
Here's a great answer to how reverse_iterator relates to the iterator. In your case, rbegin will be based on begin iterator. If you remove the begin from the list (which you do, since it has only one element), all reverse_iterators based on this iterator will become invalid. Keeping that in mind you could rewrite your loop the following way:
while ( ri != l.rend() )
{
cout << (*ri)->data << endl;
list<node*>::reverse_iterator rj = ri;
++ri;
if ( ri == l.rend() )
cout << "before erase: reached end" << endl;
// the actual underlying iterator has an offset of one
list<node*>::iterator it = l.erase(--rj.base());
ri = reverse_iterator<list<node*>::iterator>(it);
// or just
// ri = reverse_iterator<list<node*>::iterator>(l.erase(--rj.base()));
if ( ri == l.rend() )
cout << "after erase : reached end" << endl;
else
cout << "after erase : Not reached end" << endl;
}
The reverse iterator is (tpyically) not a unique class, but a adapter on a normal iterator - it has a iterator into a list as member and uses it to do its own moving and dereferencing. So when this list iterator gets invalidated, the reverse iterator is invalidated too.
i'm writing an answer to note my findings; if you hit this problem try
SingerOfTheFall 's suggested method, it works like a charm, example:
for(auto it=values.end();it!=values.begin();){
if((*it).second.endPoint)
break;
values.erase((*(it--)).first);
}
back to my findings about this:
when i hit the problem the program hanged, and i've run a valgrind check, it dropped out some strange Invalid reads originated from libstdc++
Invalid read of size 8
at 0x4EAA633: ??? (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.20)
by 0x402FEC: std::_Rb_tree_iterator<std::pair<int const, GroupControl::Group::Entry> >::operator--() (stl_tree.h:218)
i suspect that after the last element's erase the rend() doesnt stop the iterator, and the ++ op is trapped in a loop
You need store erase return value into iterator. Do following change.
(*rj)->iter= l.erase((*rj)->iter);
Related
What is the best method to return the last element in a container that does not provide a back() member function, such as std::set?
Since the end() method returns an iterator to the first element after the end of the container, is the only way of grabbing the last element to decrement the iterator before dereferencing it?
Such as:
std::set<int> set = {1,2,3,4,5};
int end = *(set.end());
int beforeEnd = *(--set.end());
std::cout << "set.end() -> " << end << std::endl;
std::cout << "--set.end() -> " << beforeEnd << std::endl;
However, these both return:
set.end() -> 5
--set.end() -> 5
Is this the proper way of getting the last element, and why do these return the same value?
This
int end = *(set.end());
As commented by πάντα ῥεῖ, has undefined behavior. That's because std::set::end
Returns an iterator to the element following the last element of the container. This element acts as a placeholder; attempting to access it results in undefined behavior.
(https://en.cppreference.com/w/cpp/container/set/end, emphasis mine)
The other line:
int beforeEnd = *(--set.end());
It's not guarateed to work. See e.g. https://en.cppreference.com/w/cpp/iterator/prev, emphasis mine:
Although the expression --c.end() often compiles, it is not guaranteed to do so: c.end() is an rvalue expression, and there is no iterator requirement that specifies that decrement of an rvalue is guaranteed to work. In particular, when iterators are implemented as pointers, --c.end() does not compile, while std::prev(c.end()) does.
So it may fail for the same reason that this wouldn't compile:
int arr[4] = {1,2,3,4};
int *p = --(arr + 4); // --> error: expression is not assignable
You can write something like the following, instead.
std::set<int> set = {1,2,3,4,5};
if ( set.begin() != set.end() )
{
auto itLast = std::prev(set.end());
std::cout << "last -> " << *itLast << '\n';
}
Is the only way of grabbing the last element to decrement the iterator
before dereferencing it?
No, there are also other options.
The simplest way is using the reverse iterators(both std::set::rbegin or std::set::crbegin), which dirctly give you the element which is one past std::sets end iterator.
From cppreference.com, std::set::rbegin and std::set::crbegin
Returns a reverse iterator to the first element of the reversed
container. It corresponds to the last element of the non-reversed
container. If the container is empty, the returned iterator is equal
to rend().
std::set<int> set = { 1,2,3,4,5 };
auto iter = set.rbegin();
const int beforeEndIter = *iter;
std::cout << "--set.end() -> " << beforeEndIter << '\n';
(update) in case of the second last element in the container(i.e. two past the end iterator), use std::next for the same reason mentioned in the other answer for std::prev. See a demo
std::set<int> set = {1, 2};
const bool hasElements = set.cbegin() != set.cend();
auto iter = set.rbegin();
if(hasElements && iter != set.rend()) std::cout << "--set.end() -> " << *iter << '\n';
if(hasElements && std::next(iter) != set.rend()) std::cout << "two past set.end() -> " << *std::next(iter) << '\n';
outputs:
--set.end() -> 2
two past set.end() -> 1
The second option has been mentioned in the other answer.
On the other hand, dereferencing the end iterator is an undefined behavior, in which you could expect any result. In your case, you have got the last element(i.e. one past end iterator) of the container.
Have searched the net a lot, but couldn't find the answer to the issue.
I am inserting a value to an std::list using its reverse_iterator. While the insertion occurs at the appropriate position as expected, what I noticed is that the value of the original reverse_iterator used for insertion changes. Also the value of a completely unrelated reverse_iterator too changes. I have been able to reproduce this in a simple example
#include <iostream>
#include <list>
#include <string>
int main()
{
// Creating a list of values 1, 2, 4, 5
std::list<int> myList;
myList.push_back(1);
myList.push_back(2);
myList.push_back(4);
myList.push_back(5);
// Changing it to 1, 2, 3, 4, 5 by inserting 3
std::list<int>::reverse_iterator revIter = myList.rbegin();
while(2 != *revIter)
{
std::cout << *revIter << "\t";
++revIter;
}
std::cout << "\n" << "Reverse iterator now points to " << *revIter;
// Creating a copy of the reverse Iter before inserting.
std::list<int>::reverse_iterator newRevIter = revIter;
myList.insert(revIter.base(), 3);
// Checking the values of revIter and newRevIter
std::cout << "\n" << "Reverse Iterator now points to " << *revIter; // UNEXPECTED RESULT HERE
std::cout << "\n" << "New Reverse Iterator now points to " << *newRevIter; // UNEXPRECTED RESULT HERE
std::cout << "\n" << "Printing final list:" << "\n";
for(std::list<int>::iterator iter = myList.begin(); myList.end() != iter; ++iter)
{
std::cout << *iter << "\t"; // Results are fine
}
return 0;
}
RESULT
5 4
Reverse iterator now points to 2
Reverse iterator now points to 3
New Reverse iterator now points to 3
Printing final list:
1 2 3 4 5
Is this expected behaviour. If so how can reverse iterator be used to insert items to a list (Or is it useless in this regard)?
I would avoid using reverse iterators (in general, and in particular for anything other than a sequential transversal). Forward and reverse iterators work differently, in the case of a forward iterator into a list, the iterator tracks the node that you access through operator*, but in the reverse case the iterator tracks the next element in the list. The act of dereferencing a reverse iterator obtains the predecessor of the node referred by the iterator and extracts the value from that. Graphically (f is a forward iterator, r is a reverse iterator)
f
1 2 4
r
Both the forward iterator f and the reverse iterator r will yield 2 when dereferenced, but the node they track is different. When you insert using r you insert between 2 and 4, but the underlying iterator is left pointing to the node holding the 4:
f
1 2 3 4
r
Now if you dereference r, the same process as above applies. The predecessor of the current node is obtained, and the value printed, except that now the predecessor of 4 is 3 and that is what you get.
Is this expected behaviour. If so how can reverse iterator be used to insert items to a list (Or is it useless in this regard)?
Yes, this is expected behavior. How can a reverse iterator be used to insert items to a list? Understanding how it works.
The invariant after insertion is the std::reverse_iterator<>::base(), not the std::reverse_iterator<> itself. But, base() does target the previous elements compared to reverse_iterator:
http://en.cppreference.com/w/cpp/iterator/reverse_iterator
What's bother me a bit is that when I had std::distance to the begin() (or rend().base() ):
std::cout << "\n"
<< "Reverse Iterator now points to "
<< *revIter << "-" << *(revIter.base())<< "-"
<< std::distance(revIter.base(), myList.rend().base());
I have:
Reverse iterator now points to 2-4-3
Reverse Iterator now points to 3-4-3
Or I expect the second one to be "3-4-4" as the element is inserted prior to the base()...
My understanding is the iterators of associative containers are not invalidated during insert or erase (unless the node pointed by iterator is erased). But in the below program
the insert seems to invalidate the iterator. Is my understanding wrong?
typedef std::set<unsigned int> myset_t;
int main(int argc, char **argv)
{
myset_t rs;
myset_t::reverse_iterator rit;
myset_t::reverse_iterator srit;
int ii = 500;
rs.insert(10);
rs.insert(11);
rs.insert(12);
rs.insert(13);
rs.insert(14);
rs.insert(100000);
rs.insert(102000);
rs.insert(103000);
rit = rs.rbegin();
while(rit != rs.rend()) {
srit = rit;
if (*rit < 100000) {
cout << "bailing here " << *rit << endl;
return 0;
}
rit++;
cout << "Before erase " << *rit << endl;
rs.erase(*srit);
cout << "Before insert " << *rit << endl;
rs.insert(ii);
cout << "After insert " << *rit << endl;
ii++;
}
cout << "Out of loop" << endl;
}
===
The output is
Before erase 102000
Before insert 102000
After insert 14
bailing here 14
=====
The promised behavior for iterators of a standard container does not hold for reverse iterators of that container.
A reverse iterator actually stores, as a member, the normal (forward moving) iterator which comes after the element to which the reverse iterator refers when dereferenced. Then when you dereference the reverse iterator, essentially it decrements a copy of this stored normal iterator and dereferences that. So this is a problem:
rit = rs.rbegin(); // rit stores rs.end()
srit = rit; // srit also stores rs.end()
rit++; // rit stores a normal iterator pointing to the last element
rs.erase(*srit); // this deletes the last element, invalidating the normal
// iterator which is stored in rit. Funnily enough, the
// one stored in srit remains valid, but now *srit is a
// different value
Reverse iterators behave this way because there is no "before begin" iterator. If they stored the iterator to the element to which they actually refer, what would rs.rend() store? I'm sure there are ways around this, but I guess they required compromises which the standards committee was not willing to make. Or perhaps they never considered this problem, or didn't consider it significant enough.
I have taken this little snippet straight out of some code I'm working on:
KeyIter it = timeline_.lowerBound( frame );
if ( timeline_.isKeyAtFrame( frame ) ) {
++it;
}
KeyIter it1 = it - 1;
cout << "dist1: " << std::distance( timeline_.begin(), it1 ) << endl;
while ( ignore.contains( it1.key() ) ) {
cout << "dist2: " << std::distance( timeline_.begin(), it1 - 1 ) << endl;
if ( std::distance( timeline_.begin(), --it1 ) < 0 ) {
break;
}
}
cout << "dist3: " << std::distance( timeline_.begin(), it1 ) << endl;
It gives this output:
dist1: 0
dist2: 2
dist3: 2
ignore is a QSet<int> and it1 is an iterator for timeline_ (it's map type with a key of int). As you can see it1 starts at the beginning (this is correct), then control goes into the while loop where the iterator is moved backwards by one; but instead of std::distance being -1, it's 2! All that happens inbetween is a copy of the key is used to check if the QSet contains the same int.
Using a debugger I can confirm that timeline_ does not change inbetween the two dist# outputs (only one thread is running at this point in the code anyway).
Can anyone see why std::distance would be giving this output?
I'm not sure about Qt's behavior on this, but in standard library containers, aquiring an iterator outside the range [container.begin(),container.end()] is undefined behavior. I would assume it's the same in Qt, though I'm not sure. However, even if it's not, the behavior of std::distance on non-random access iterators is to count the number of increments required to get from the first iterator to the last, so this:
std::distance(x,y)
where y precedes x, is undefined behavior.
I was trying to use the vector STL, where iam facing a weird response from the following sample program :
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
vector<int>::iterator fun();
vector<int> myvector;
bool IsOdd (int i) {
return ((i%2)==1);
}
int main()
{
vector<int>::iterator it;
it = fun();
if (it == myvector.end() )
cout << "Reached end, Global" << endl;
else
cout << "Not end" << endl;
}
vector<int>::iterator fun() {
vector<int>::iterator it;
myvector.push_back(10);
myvector.push_back(26);
myvector.push_back(40);
myvector.push_back(56);
it = find_if (myvector.begin(), myvector.end(), IsOdd);
cout << "The first odd value is " << *it << endl;
if (it == myvector.end() )
cout << "Reached end, inside the function" << endl;
else
cout << "Not end" << endl;
return it;
}
Iam getting "Reached End" inside the function fun(), whereas, in the main program, it is showing up as "Not End".
Not sure, what might be the reason. Also, found that, the Address of myvector.end() is showing up as Zero in the main program [ after the fun() call ], where-as, showing a non-zero value inside the function fun().
The function is using the local myvector, main is using the global one.
Your modified code produces:
Reached end, inside the function
Reached end, Global
as expected.
Edit: Well, not as expected - as others have pointed out:
it = find_if (myvector.begin(), myvector.end(), IsOdd);
cout << "The first odd value is " << *it << endl;
will cause undefined behaviour with your dataset, because you don't have any odd values. You want:
it = find_if (myvector.begin(), myvector.end(), IsOdd);
if ( it != myvector.end() ) {
cout << "The first odd value is " << *it << endl;
}
There are two different myvectors, one is global, another is in fun. Therefore, you're comparing an iterator to the global vector with an iterator to a local vector which moreover doesn't exist anymore.
You have two instances of vector with name myvector. One global and one local to function fun. Inside fun this local vector hides the global vector. Since you are operating on two totally different vector objects you are seeing these results.
You dereferenced *it without checking if it was before the end. None of your vector values are odd, so it will be end and you will cause undefined behaviour by dereferencing it.