简体   繁体   中英

How can I erase elements from a vector given a list of iterator?

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:

  1. Iterators are invalidated only when there is a reallocation.
  2. Just replace iterators with index does not change the fact that they are invalid when some values are removed from the vector.
  3. Using indexes is still a better approach as they don't get invalidated when more elements are added later.

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++;
            }
        }            
    }
};

Link: https://godbolt.org/g/SouZ5E

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM