I have a vector of int, and a map which contains as values some iterators pointing to the vector. I need to remove keys from the map, and the vector element the value points to. My code look briefly like that:
using RenderData = int;
using Element = std::string;
struct Ref {
std::vector<RenderData>::iterator ref;
std::function<int()> update;
bool should_remove;
};
int main() {
std::vector<RenderData> ints{1, 2, 3, 4, 5, 6, 7, 8, 9};
std::unordered_map<Element, Ref> elements;
// Here, I need to remove some elements, and their associated number
}
I implemented an erase_if
function that looked like this one .
So my initial code looked like that:
erase_if(elements, [&](auto&& element) {
if (element.second.should_remove) {
ints.erase(element.second.ref);
return true;
}
return false;
});
It obviously didn't worked. Erasing element makes other iterator to point on wrong object, and in some case out of bound. So I tried that:
std::vector<std::vector<RenderData>::iterator> to_remove;
erase_if(elements, [&](auto&& element) {
// condition based on the string content
if (element.second.should_remove) {
to_remove.emplace_back(element.second.ref);
return true;
}
return false;
});
// Sort in descending order
std::sort(to_remove.begin(), to_remove.end(), std::greater<>{});
// stuff
for (auto&& it : to_remove) {
ints.erase(it); // nothing can go wrong right?
}
And again, I ended up erasing the wrong elements sometimes.
Given iterators stored in some map, can I remove from the vector the elements the iterators are pointing to?
Update:
It seems in the last snippet I swapped some elements in the vector, making erasing the wrong elements. Now it seem to work but I'm still curious what are the methods we can do to erase elements in a vector from an iterator list.
Plan A : You'd better change std::vector with std::list, the iterator of std::list won't be invalid after erase.
Plan B:
std::vector<int> newOne;
copy_if(oldOne.begin(), oldPOne.end(), std::back_inserter(newOne), [](){ //to test not in the map ~~ });
oldOne = std::move(newOne)
Use indices instead of iterators. Replace
struct Ref {
std::vector<RenderData>::iterator ref;
std::function<int()> update;
bool should_remove;
};
std::vector< std::vector<RenderData>::iterator> to_remove;
with
struct Ref {
std::size_t ref;
std::function<int()> update;
bool should_remove;
};
std::vector< std::size_t> to_remove;
And then your good idea with descendant sort will be working. To erase an element by index call ints.erase(ints.begin() + ref)
Sorting of iterators is a wrong idea. How vector iterators are implemented is not predictable. It is a very small probability that their sort order is the same as for pointers/addresses/indices.
iterator erase( iterator pos ); (until C++11)
iterator erase( const_iterator pos ); (since C++11)
Removes the element at pos.
Invalidates iterators and references at or after the point of the erase, including the end() iterator.
Using the iterator version. Note:
Approach: Create a copy of vector with elements that should not be removed
using Element = std::string;
using RenderData = int;
struct Ref {
std::vector<RenderData>::iterator itr;
bool should_remove;
};
struct Main {
std::vector<RenderData> ints;
std::unordered_map<Element, Ref> elements;
void remove_stuff(){
std::vector<RenderData> localCopy;
localCopy.swap(ints);
ints.reserve(localCopy.size());
for(auto it = elements.begin(); it != elements.end();) {
Ref& ref = it->second;
if(ref.should_remove) {
it = elements.erase(it);
} else {
ints.push_back(std::move(*ref.itr));
ref.itr = ints.end() - 1;
it++;
}
}
}
};
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.