I'm currently writing a program that uses lists at a point in said program i want to iterate through 3 three lists a, b and c, and delete any element in b and c if it appears in a. Im doing it as such:
//remove elements from OpenList that are in ClosedList
for(list<Node> :: iterator cloIt = ClosedList.begin(); cloIt != ClosedList.end(); cloIt++)
{
for(list<Node> :: iterator opIt = OpenList.begin(); opIt != OpenList.end(); opIt++)
{
for(list<Node> :: iterator neigIt = Neighbour.begin(); neigIt != Neighbour.end(); neigIt++)
{
if (*cloIt == *opIt)
{
opIt = OpenList.erase(opIt);
}
if (*cloIt == *neigIt)
{
neigIt = Neighbour.erase(neigIt);
}
}
}
}
However this is causing me to get an "List iterator not incrementable" error
How could i fix this?
From your erase call, you want to
remove OpenList items if they are found in ClosedList list
remove Neighbour items if they are found from ClosedListlist
You'd better separate code into two loops, instead of nested loops, for example:
1.remove OpenList items if they are found in ClosedList list
for(auto cloIt = ClosedList.begin(); cloIt != ClosedList.end(); ++cloIt)
{
OpenList.remove_if([&](const Node& n){ return n == *colIt; } );
}
2.remove Neighbour items if they are found from ClosedListlist
for(auto cloIt = ClosedList.begin(); cloIt != ClosedList.end(); ++cloIt)
{
Neighbour.remove_if([&](const Node& n){ return n == *colIt; } );
}
Obvious previous code is duplicated, you could write a common function for that:
void RemoveItem(std::list<Node>& node_list, std::list<Node>& node_list2)
{
for(auto cloIt = node_list2.begin(); cloIt != node_list2.end(); ++cloIt)
{
node_list.remove_if([&](const Node& n){ return n == *colIt; } );
}
}
Now you could call:
RemoveItem(OpenList, CloseList);
RemoveItem(Neighbour, CloseList);
Update:
Don't forget to define operator== for Node type, for example if node has getId interface:
bool operator==(const Node& lhs, const Node& rhs)
{
return lhs.getId() == rhs.getId();
}
How could i fix this?
The best way is to use standard algorithms and let them do the iteration, search, and/or the conditional removal for you.
You could use std::list's remove_if() member function with a lambda predicate that checks if the element is contained in list a:
#include <algorithm>
// ...
b.remove_if(
[&a] (Node const& n)
{
return (std::find(begin(a), end(a), n) != a.end());
});
Same for removing elements from c if they are contained in a.
Another possibility is to use std::for_each() to iterate over all elements of a and remove them from b and c:
#include <algorithm>
// ...
std::for_each(begin(a), end(a),
[&b, &c] (Node const& n)
{
b.remove(n);
c.remove(n);
});
You've correctly used the return value of .erase to obtain the new iterator, but forgot that this iterator gets ++'d immediately at the end of the current iteration of your loop; if the result of .erase was .end, then this is an invalid operation.
(You're actually very fortunate that you get a diagnostic for attempting to increment your now-invalid iterators — the standard guarantees absolutely nothing about this case.)
You need to ++ only when you didn't .erase.
The general pattern looks like this:
for (typename list<T>::iterator it = l.begin(), end = l.end(); it != end; )
{
// ^^ NB. no "it++" in the loop introduction!
if (foo(*it)) {
// condition satisfied; do the erase, and get the next
// iterator from `.erase` and NOT through incrementing
it = l.erase(it);
}
else {
// no erasure; do the increment only in this case
it++;
}
}
You could avoid the problem altogether by using standard algorithms, as Andy suggests.
Related
I got a method that adds adjacent Voxels to a vector. This method uses an vector with all the remaining points (means, they still need to be looked at as they are possible adjacents) and looks on every one of them if it is near enough to be added. If an element is a neighbor it also checks all adjacents of this element to add them too. This happens in a recursive manner.
void remove(std::vector<pcl::PointXYZ> &vec, pcl::PointXYZ p) {
for (int i = 0; i < vec.size(); i++) {
if (vec[i].x == p.x && vec[i].y == p.y && vec[i].z == p.z) {
vec.erase(vec.begin() + i);
break; // as all points should be unique
}
}
}
void addAdjacents(pcl::PointXYZ start, std::vector<pcl::PointXYZ> &newCluster, std::vector<pcl::PointXYZ> &remainingPoints) {
for (pcl::PointXYZ p : remainingPoints) {
if (distance(p, start) < 0.015) {
newCluster.push_back(p);
remove(remainingPoints, p);
if (remainingPoints.size() > 0)
addAdjacents(p, newCluster, remainingPoints);
}
}
}
The problem is, that many points from the remainingPoints-vector are added to the newCluster multiple times. I thought this wouldn't happen but it seems like internally it makes copies of the vector in the recursion? If a point is removed in a deeper layer the for-loop in the outer execution is somehow still iterating over this (removed) element.
I am fairly new to c++ so I am not sure how to prevent this. Can anyone help me? Thanks!
For sure I can just write a method addToCluster which just checks if the vector has this element before adding it but I thought that maybe there is a more elegant way to prevent this happening in the first place.
Edit:
As I understand I am breaking my iterator in the loop. So I would need to somehow update my iterator after calling addAdjacents. Is this right? Can I do something like that?
You need to separate identifying the points you wish to migrate with erasing them from the input.
template<typename BidirIt>
BidirIt addAdjacentsImpl(pcl::PointXYZ start, std::vector<pcl::PointXYZ> &newCluster, BidirIt first, BidirIt last) {
auto part = std::stable_partition(first, last, [&](auto p){ return distance(p, start) >= 0.015; });
for (auto it = part; it != last; ++it) {
newCluster.push_back(*it);
part = addAdjacentsImpl(*it, newCluster, first, part);
}
return part;
}
This only re-orders the elements, such that those we wish to remove are after those we wish to keep. I've written it as a template because I don't care to name the particular iterator types.
void addAdjacents(pcl::PointXYZ start, std::vector<pcl::PointXYZ> &newCluster, std::vector<pcl::PointXYZ> &remainingPoints) {
auto last = addAdjacentsImpl(start, newCluster, remainingPoints.begin(), remainingPoints.end());
remainingPoints.erase(last, remainingPoints.end());
}
Here
list.erase(list.begin() + i);
You erase an element from the std::vector called list (really not the best name for a vector) which invalidates iterators at and after the erased position. It isnt that obvious, but this erasing happens while addAdjacents iteratates over the same container. Leaving only the broken part in we have
void addAdjacents(pcl::PointXYZ start, std::vector<pcl::PointXYZ> &newCluster, std::vector<pcl::PointXYZ> &remainingPoints) {
for (pcl::PointXYZ p : remainingPoints) {
newCluster.push_back(p);
//remove(remainingPoints, p); // <- calls erase
remainingPoints.erase( remainingPoints.begin() + some_index);
}
}
}
Erasing an element from remainingPoints does break the range-based loop, because under the hood it uses iterators that potentially got invalidated in remove.
I have some code with lambda expression and my problem is that I would like to return something only if "if" isn't nullptr. Other way I don't want to return anything. Is there a return that can be used in this code? Or maybe another way to not getting warning?
auto iter = std::stable_partition(object1->vector_.begin(), object1->vector_.end(), [](Class* x)
{
if (x->object2_ != nullptr)
{
return !x->object2->parameter_;
}
});
It's working good, but this warning is annoying and I know that I should do something with this.
The intent of std::stable_partition is:
Reorders the elements in the range [first, last) in such a way that all elements for which the predicate p returns true precede the elements for which predicate p returns false. Relative order of the elements is preserved.
When you use
return !x->object2->parameter_;
you want to put all the elements for which !x->object2->parameter_ is true to the left and all the elements for which !x->object2->parameter_ is false to the right.
Judging by that, I would say if x->object2_ is a nullptr, you should put them to the right. Hence, the default return needs to be
return false;
Hence,
auto iter = std::stable_partition(object1->vector_.begin(),
object1->vector_.end(),
[](Class* x) -> bool
{
if (x->object2_ != nullptr)
{
return !x->object2->parameter_;
}
return false;
});
You can combine the body of the function to one line as:
auto iter = std::stable_partition(object1->vector_.begin(),
object1->vector_.end(),
[](Class* x) -> bool
{
return (x->object2_ != nullptr) && (!x->object2->parameter_);
});
I have a structure like this:
struct client
{
string name;
double money;
};
I also have 2 predicates:
bool less_10(const client& a)
{
return a.money < 10;
}
bool not_a(const client& a)
{
return a.name.at(0) != 'A';
}
In my main function I use this to filter out the result stored in vector client_list (everyone with money < 10 (choice 1) or everyone with name not start with A (else))
if (choice_filter == 1)
{
vector<client>::iterator it3;
it3 = find_if(client_list.begin(), client_list.end(), less_10);
while (it3 != client_list.end())
{
**client_list.erase(it3);
it3 = find_if(it3 + 1, client_list.end(), less_10);
}
client_list.erase(it3);**
}
else
{
vector<client>::iterator it4;
it4 = find_if(client_list.begin(), client_list.end(), not_a);
while (it4 != client_list.end())
{
**client_list.erase(it4);
it4 = find_if(it4 + 1, client_list.end(), not_a);
}
client_list.erase(it4);**
}
I notice that if I erase first, then find_if, i'll lost the last client. So i added 1 more line to erase, but the program crashes as iterator is now at the end, cant erase.
Is there any way to get around this? I want to keep using find_if with predicates as well as while loop like above as they are required.
As others have said, std::remove_if is the best solution. If
you're doing this for pedagogical reasons (which I suspect is
the case, given these particular predicates): you're on the
right track. The only issue is that client_list.erase
invalidates the iterator. But since it returns an iterator to
the element immediately after the element it erased, you can use
something like:
std::vector<Client>::iterator it
= std::find_if( client_list.begin(), client_list.end(), predicate );
while ( it != client_list.end() ) {
it = client_list.erase( it );
it = std::find_if( it, client_list.end(), predicate );
}
And you don't want to call erase after the loop. The iterator
designates the end, where there is no element to be erased.
The typical way to go is to use a temporary vector:
vector<client> tmp;
for (...)
{
if(predicate(it))
tmp.push_back(*it);
}
client_list.swap(tmp);
This is similar to what Chris suggested in a comment, although that solution would first move elements to the end of the vector and then truncate them from there. I'm not sure if that doesn't change the order on the way, just check the documentation. Depending on what you want, either could do the work though.
If you used a different container like list<> that did not invalidate all iterators in erase(), you could do this:
it = c.begin();
end = c.end();
while(it != end)
{
if(predicate(*it))
{
c.erase(it++);
}
else
{
++it;
}
}
Note that if you call erase(), you invalidate that iterator still, hence the iterator is first incremented and erase() is called with the former value using the postfix increment.
I also agree with chris, to using std::remove_if:
{
remove_if(client_list.begin(), client_list.end(), less_10);
}
But if you want to reinvent the wheel:
{
vector<client>::iterator it3 = client_list.begin();
while (true)
{
it3 = find_if(it3, client_list.end(), less_10);
if (it3 == client_list.end()) {
break;
}
it3 = client_list.erase(it3);
}
}
How can i delete all objects which are works finished
I using the following code but get list iterator not incrementable
How can I remove it without deleting it
list<A*> myList;
for(list<A*>::iterator it = myList.begin(); it !=myList.end(); ++it ){
(*it )->DoSomething();
if((*it )->WorksFnished()){
//myList.erase(it ); <- It's works but I get exception after the loop
//myList.remove(*it ); <- It's works but I get exception after the loop
}
}
erase returns an iterator
list<A*> myList;
list<A*>::iterator it = myList.begin();
while( it != myList.end() ) {
(*it)->DoSomething();
if( (*it)->WorksFnished() ) {
it = myList.erase(it);
} else {
++it;
}
}
You can make use of the fact that erase returns a new iterator, as described in other answers here. For performance-critical code, that might be the best solution. But personally, I would favor splitting the loop into separate processing and removal steps for readability and clarity:
// Assumes C++ 11 compatible compiler
list<A*> myList;
// Processing
for(const auto* each : myList){
each->DoSomething();
}
// Deletion
myList.remove_if([](A* each) {
return each->WorksFnished();
});
If you don't want to use remove_if, some alternatives are:
Copy all objects you want to keep into a new list, then std::swap it with your current list
Use a temporary list toBeRemoved, and add all objects that should be removed to that. When you're finished iterating over the actual list, iterate toBeRemoved and call myList.erase for each element
Some workaround..
increment the number of objects from the list that has WorkFnished.
then after the loop. if the accumulator match the list size, clear it.
size_t nFinished = 0;
list<A*> myList;
for(list<A*>::iterator it = myList.begin(); it !=myList.end(); ++it ){
(*it )->DoSomething();
if((*it )->WorksFnished()){
nFinished++;
}
}
if (nFinished == myList.size())
{
myList.clear();
}
If you use erase you have to assign it back to the iterator. In this case, we have to take care of the incrementing ourselves depending if the current element was erased or not.
list<A*> myList;
for (auto it = myList.begin(); it != myList.end(); )
{
(*it)->DoSomething();
if( (*it)->WorksFnished() ) {
it = myList.erase(it); // Sets it to the next element
} else {
++it; // Increments it since no erasing
}
}
std::list::erase
Return: An iterator pointing to the new location of the element that followed the last element erased by the function call. This is the container end if the operation erased the last element in the sequence.
My problem is as follows: I use an iterator, and I want to compare each element to the next element. Prototype looks like below, how can I increase the iterator to be able to compare?
Also, how can I set a proper condition for this to happen? I mean how to point on the last element, not on the next after the last like with end() function:
std::vector<T>::const_iterator it;
std::vector<T>::const_iterator it2;
for (it = set.begin(), it != set.end(); it++)
{
// some things happen
if ( final == it )
{
if ( it != set.end()-1 ) // how to write properly condition?
{
it2 = it + 1; //how to assign the next here?
if (...)//some condition
{
if ( it->func1() - it2->func1()) < 20 ) //actual comparison of two consecutive element values
// do something
}
}
}
}
In C++11 use the functions std::next() and std::prev().
Your code could become:
// before
it != std::set.end()-1
// after
it != std::prev(set.end())
and
// before
it2 = it + 1;
// after
it2 = std::next(it);
That is true also for non-vector containers, such as map,set or others.
NOTE: after std::next(it), "it" iterator remains unmodified!
NOTE 2: Use it2 = std::next(it,n); to increment as much as you need.
You can use adjacent_find to solve that. You should use the second form of that function (with predicate) and pass to the predicate your some things happen and some condition in c-tor
auto found = std::adjacent_find( set.begin(), set.end(),
[some_comdition]( const T & left, const T & right ) {
if ( some_comdition ) {
if ( left.func1() - right.func1() < 20 ) {
do_smth();
// return true; if there's no need to continue
}
}
return false;
}
);
Based on the fact that it++ is acceptable, we should define a new iterator called itplusone, which is initialized as itplusone = ++it. In this way, you can safely use the meaning of an iterator pointing to the next item of it. Also clearly, the range of iterator of itplusone bounded by terms itplusone != set.end(). I use this method to compute the total weight of a path, which is defined as a list object.
In the for loop, you use it++ which means it = it + 1, which is perfectly ok. So this one will be fine also it2 = it + 1. it2 will be pointing to the next value.
In the for loop again, you use it != set.end(), which is again perfectly ok. So you can also it + 1 < set.end(), just like you did in your code.
I don't see anything wrong in your code, just wanted to explain.
somewhat late, just discovered it, but like mentioned above, ++ iterator works fine.
vector<string> P
auto itA = begin(P);
while(itA != end(P))
{
if(itA != end(P))
{
++itA; //
}
}