List iterators not compatible - c++

I have this for:
for (list<Docente>::iterator it3 = ((*it2).getDocentes()).begin(); it3 != ((*it2).getDocentes()).end(); it3++)
This ((*it2).getDocentes) returns a list of Docentes. So why does it say that list iterators are incompatible? Any help?
Method getDocentes() :
list <Docente> getDocentes() const;
list <Docente> EdicaoDisciplina :: getDocentes() const
{
return docentes;
}
Error:
Expression: list iterators incompatible

You haven't provided enough information.
If you get that typical MS Visual C++ run-time assertion about incompatible iterators, then I suspect that your getDocentes() returns the list by value, i.e. it returns a temporary copy of the original container. That means that begin() is called on one temporary copy and end() is called on a completely different temporary copy. That makes the iterators returned by begin() and end() incompatible, since it is illegal to compare iterators that point into different containers. Debug versions of some run-time libraries can catch such errors and issue run-timer assertion failures.
It is also possible that the same problem happens even earlier, because of *it2 returning its result by value.
EDIT: Just as I guessed, your getDocentes() returns its result by value. This is virtually meaningless and catastrophically inefficient. Such heavy objects as lists are not supposed to be passed around by value. Return it by reference
const list <Docente>& EdicaoDisciplina :: getDocentes() const
{
return docentes;
}
Now you have to use const_iterator instead of iterator (and lose all these excessive pairs of ())
for (list<Docente>::const_iterator it3 = it2->getDocentes().begin();
it3 != it2->getDocentes().end();
++it3)
...
Finally, evaluating it2->getDocentes().end() on every iteration is not a good idea either
for (list<Docente>::const_iterator
it3 = it2->getDocentes().begin(),
it3_end = it2->getDocentes().end();
it3 != it3_end;
++it3)
...

Every time you call getDocentes() you're getting back a new list, and you can't validly compare iterators across different STL containers.
In your case, there is a list that gets returned when you call getDocentes().begin(), and then there is a new list created for every loop iteration, when you call getDocentes().end().
You could either use a range-based for loop, if you have C++11, or you could just cache the begin and end iterators before entering you for loop:
list <Docente> myDocentes(getDocentes());
list <Docente>::iterator begin = myDocentes.begin();
list <Docente>::iterator end = myDocentes.end();
for(list<Docente>::iterator i = begin; i != end; i++) { .... }
Or, just use the range based for:
for(auto &it : getDocentes()) { ... }

Related

Trouble using iterator on a list of objects

So I have a class called symbol, which is made up of 4 strings which are all public. I created a list of these and I want to do a look ahead on this list. This is what I have so far. I looked up the iterator methods and it says it supports the + operator but I get an error for this.
bool parser::parse(list<symbol> myList){
//Will read tokens by type to make sure that they pass the parse
std::list<symbol>::const_iterator lookAhead = myList.begin();
if ((lookAhead + 1) != myList.end)
lookAhead++;
for (std::list<symbol>::const_iterator it = myList.begin(); it != myList.end(); ++it){
if (it->type == "") {
}
}
return true;
}
I get an error when trying to add 1 to lookAhead. What are some good ways of creating a look ahead for a list?
Thanks,
Binx
A linked list does not support random access iterators, i.e. you cannot add an integer to its iterators.
Use std::next(lookAhead) to get the next iterator instead, or std::advance(lookAhead, 1). These functions know what kind of iterator is being passed, and will use a random seek if possible (e.g. with std::vector's random-access iterators), or manually advance (with a loop in the case of std::advance()) otherwise, as in this case.
Be careful advancing on iterators unconditionally, though -- advancing past end() is undefined!
You can read more about the different categories of C++ iterators here.
Side note: You're copying the entire list when it's passed in, since you're passing it by value. You probably want to pass it by reference instead (list<symbol> const& myList). You can also simplify your code using the C++11 auto keyword, which deduces the type automatically from the type of the expression that initializes the variable:
bool parser::parse(list<symbol> const& myList){
// Will read tokens by type to make sure that they pass the parse
auto lookAhead = myList.begin();
if (lookAhead != myList.end() && std::next(lookAhead) != myList.end())
++lookAhead;
for (auto it = myList.begin(); it != myList.end(); ++it){
if (it->type == "") {
}
}
return true;
}

Accessing an element and its successor in a list at the same time

I have a list and when iterating over it i want to access the element at the iterator and the following element at the iterator+1 at the same time. Here is my attempt:
std::list<Team*> teamlist = league.GetTeamMembers();
for (std::list<Team*> ::iterator iterator = teamlist.begin(); iterator != teamlist.end(); std::advance(iterator,2))
{
match(*(*iterator), *(*(++iterator)));
}
The match function does nothing to the iterators, it just get's some values of the teams out of it to calculate a match result.
But the ++iterator does not work, as the elements in the matchfunction are still the same. How do I do this?
Passing the iterator and operating on the iterator in the same pass (like increment of the iterator) leads to undefined behavior. There are certain concepts such as sequence points, which you should be aware of when you are performing such operations.
Further, you can check this link also. I suggest you to move the increment of the operator after the pass to function. Then it should work fine.
You could avoid increasing the iterator at increment part of the loop and dot it in the body:
std::list<Team*> teamlist = league.GetTeamMembers();
for (std::list<Team*> ::iterator it = teamlist.begin();
it != teamlist.end(); /*Nothing here*/)
{
match(*(*it), *(*(++it))); //Which parameter expression is evaluated first isn't determined
++it;
...
...
EDIT:
As FredOverflow has pointed out, match parameter expressions evaluations are not guaranteed to run in the left to right order. To avoid this hazardous situation:
std::list<Team*> teamlist = league.GetTeamMembers();
for (std::list<Team*> ::iterator it = teamlist.begin();
it != teamlist.end(); /*Nothing here*/)
{
Team *pa = *it;
Team *pb = *(++it);
match(*pa, *pb);
++it;
...
...
you are increasing the iterator twice, first in the head of the for-loop:
std::advance(it,2)
then in the loop-body, where you do a:
++it
Is this really what you want? It looks pretty confusing to me.
If you want the element next to it, but dont want to increase it, better use:
auto nextIt = std::next(it);
Also: What does the match-function do? Are you sure its implemented right and not the source of the bug?
Hope this helps
Alexander

std::list::erase not working

I am trying to delete an element from a list of objects if one of the object's properties matches a condition. This is my function to do so, however, after performing this operation and then printing the contents, the erase() seems to have no effect. What am I doing wrong here?
void FileReader::DeleteProcess(int id, list<Process> listToDeleteFrom)
{
list<Process>::iterator process;
for(process = listToDeleteFrom.begin(); process != listToDeleteFrom.end(); process++)
{
if (process -> ID == id)
{
listToDeleteFrom.erase(process);
}
}
}
First, you need to pass the list by reference; your code is working on a copy, so changes it makes won't affect the caller's list:
void FileReader::DeleteProcess(int id, list<Process> & listToDeleteFrom)
^
Second, erasing a list element invalidates any iterator that refers to that element, so attempting to carry on iterating afterwards will cause undefined behaviour. If there will only be one element to remove, then return from the function straight after the call to erase; otherwise, the loop needs to be structured something like:
for (auto it = list.begin(); it != list.end(); /* don't increment here */) {
if (it->ID == id) {
it = list.erase(it);
} else {
++it;
}
}
Calling erase() when an iterator is iterating over the list invalidates the iterator. Add the elements to erase to a second list then remove them afterwards.
Also note that you are passing the list by value rather than using a reference or a pointer. Did you mean to use list<Process>& listToDeleteFrom or list<Process>* listToDeleteFrom?
The reason you see no changes reflected is that your list is not being passed by reference, so you are only removing elements from a copy of the list.
Change it to this:
void FileReader::DeleteProcess(int id, list<Process> &listToDeleteFrom) //note &
This will keep the same syntax in the function and modify the original.
However, the way you're deleting the elements is a bit sub-optimal. If you have C++11, the following will remove your invalidation problem, and is more idiomatic, using an existing algorithm designed for the job:
listToDeleteFrom.erase ( //erase matching elements returned from remove_if
std::remove_if(
std::begin(listToDeleteFrom),
std::end(listToDeleteFrom),
[](const Process &p) { //lambda that matches based on id
return p->ID == id;
}
),
std::end(listToDeleteFrom) //to the end of the list
);
Note the keeping of std::list<>::erase in there to actually erase the elements that match. This is known as the erase-remove idiom.

Why does a push_back on an std::list change a reverse iterator initialized with rbegin?

According to some STL documentation I found, inserting or deleting elements in an std::list does not invalidate iterators. This means that it is allowed to loop over a list (from begin() to end()), and then add elements using push_front.
E.g., in the following code, I initialize a list with elements a, b and c, then loop over it and perform a push_front of the elements. The result should be cbaabc, which is exactly what I get:
std::list<std::string> testList;
testList.push_back("a");
testList.push_back("b");
testList.push_back("c");
for (std::list<std::string>::iterator itList = testList.begin(); itList != testList.end(); ++itList)
testList.push_front(*itList);
for (std::list<std::string>::const_iterator itList = testList.begin(); itList != testList.end(); ++itList)
std::cout << *itList << std::endl;
When I use reverse iterators (loop from rbegin() to rend()) and use push_back, I would expect similar behavior, i.e. a result of abccba. However, I get a different result:
std::list<std::string> testList;
testList.push_back("a");
testList.push_back("b");
testList.push_back("c");
for (std::list<std::string>::reverse_iterator itList = testList.rbegin(); itList != testList.rend(); ++itList)
testList.push_back(*itList);
for (std::list<std::string>::const_iterator itList = testList.begin(); itList != testList.end(); ++itList)
std::cout << *itList << std::endl;
The result is not abccba, but abcccba. That's right there is one additional c added.
It looks like the first push_back also changes the value of the iterator that was initialized with rbegin(). After the push_back it does not point anymore to the 3rd element in the list (which was previously the last one), but to the 4th element (which is now the last one).
I tested this with both Visual Studio 2010 and with GCC and both return the same result.
Is this an error? Or some strange behavior of reverse iterators that I'm not aware of?
The standard says that iterators and references remain valid during an insert. It doesn't say anything about reverse iterators. :-)
The reverse_iterator returned by rbegin() internally holds the value of end(). After a push_back() this value will obviously not be the same as it was before. I don't think the standard says what it should be. Obvious alternatives include the previous last element of the list, or that it stays at the end if that is a fixed value (like a sentinel node).
Technical details: 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.
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).
I think to understand this, it's best to start by re-casting the for loop as a while loop:
typedef std::list<std::string> container;
container testList;
testList.push_back("a");
testList.push_back("b");
testList.push_back("c");
container::reverse_iterator itList = testList.rbegin();
while (itList != testList.rend()) {
testList.push_back(*itList);
++itList;
}
Along with that, we have to understand how a reverse_iterator works in general. Specifically a reverse_iterator really points to the element after the one you get when you dereference it. end() yields an iterator to just after the end of the container -- but for things like arrays, there's no defined way to point to just before the beginning of a container. What C++ does instead is have the iterator start from just after the end, and progress to the beginning, but when you dereference it, you get the element just before where it actually points.
That means your code actually works like this:
After that, you get pretty much what you expect, pushing back B and then A, so you end up with ABCCCBA.
Try using an iterator for both. Try:
std::list<std::string>::iterator i = testList.end();
and reverse through with --i

How do you iterate backwards through an STL list?

I'm writing some cross-platform code between Windows and Mac.
If list::end() "returns an iterator that addresses the location succeeding the last element in a list" and can be checked when traversing a list forward, what is the best way to traverse backwards?
This code workson the Mac but not on Windows (can't decrement beyond first element):
list<DVFGfxObj*>::iterator iter = m_Objs.end();
for (iter--; iter!=m_Objs.end(); iter--)// By accident discovered that the iterator is circular ?
{
}
this works on Windows:
list<DVFGfxObj*>::iterator iter = m_Objs.end();
do{
iter--;
} while (*iter != *m_Objs.begin());
Is there another way to traverse backward that could be implemented in a for loop?
Use reverse_iterator instead of iterator.
Use rbegin() & rend() instead of begin() & end().
Another possibility, if you like using the BOOST_FOREACH macro is to use the BOOST_REVERSE_FOREACH macro introduced in Boost 1.36.0.
The best/easiest way to reverse iterate a list is (as already stated) to use reverse iterators rbegin/rend.
However, I did want to mention that reverse iterators are implemented storing the "current" iterator position off-by-one (at least on the GNU implementation of the standard library).
This is done to simplify the implementation, in order for the range in reverse to have the same semantics as a range forward [begin, end) and [rbegin, rend)
What this means is that dereferencing an iterator involves creating a new temporary, and then decrementing it, each and every time:
reference
operator*() const
{
_Iterator __tmp = current;
return *--__tmp;
}
Thus, dereferencing a reverse_iterator is slower than an normal iterator.
However, You can instead use the regular bidirectional iterators to simulate reverse iteration yourself, avoiding this overhead:
for ( iterator current = end() ; current != begin() ; /* Do nothing */ )
{
--current; // Unfortunately, you now need this here
/* Do work */
cout << *current << endl;
}
Testing showed this solution to be ~5 times faster for each dereference used in the body of the loop.
Note: Testing was not done with the code above, as that std::cout would have been the bottleneck.
Also Note: the 'wall clock time' difference was ~5 seconds with a std::list size of 10 million elements. So, realistically, unless the size of your data is that large, just stick to rbegin() rend()!
You probably want the reverse iterators. From memory:
list<DVFGfxObj*>::reverse_iterator iter = m_Objs.rbegin();
for( ; iter != m_Objs.rend(); ++iter)
{
}
As already mentioned by Ferruccio, use reverse_iterator:
for (std::list<int>::reverse_iterator i = s.rbegin(); i != s.rend(); ++i)
This should work:
list<DVFGfxObj*>::reverse_iterator iter = m_Objs.rbegin();
for (; iter!= m_Objs.rend(); iter++)
{
}