Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to efficiently replace elements in an unordered_set while iterating over it?

Suppose you have an

std::unordered_set<std::shared_ptr<A>> as;
// (there is an std::hash<std::shared_ptr<A>> specialisation)

and you want to replace some of its elements while iterating over it:

for (auto it = as.begin(); it != as.end(); ++it) {
  if ((*it)->condition()) {
    as.erase(it);
    as.insert(std::make_shared<A>(**it));
  }
}

This could invalidate the iterator at erase and insert (if rehashing takes place), so this loop will exhibit undefined behaviour and will most likely crash horribly.

The one solution I can think of is using two separate vectors to buffer the insert and erase operations and later use the overloads that take iterator pairs for erasing and inserting (this is presumably more rehashing-friendly).

Even if I use the buffer approach, this still seems bloated code and could result in two rehashes that might possibly both be unnecessary.

So, is there a better way to do it?

like image 882
bitmask Avatar asked Apr 22 '26 22:04

bitmask


1 Answers

I just thought of a possible approach (just after asking) but maybe there are even better ones.

Copying everything to a vector and then rebuilding the set from the vector should be faster:

std::vector<std::shared_ptr> buffer;
buffer.reserve(as.size());
for (auto it = as.begin(); it != as.end(); ++it) {
  if ((*it)->condition()) {
    buffer.push_back(std::make_shared<A>(**it));
  } else {
    buffer.push_back(*it);
  }
}
as = std::unordered_set<std::shared_ptr<A>>(buffer.begin(),buffer.end());
like image 107
bitmask Avatar answered Apr 24 '26 10:04

bitmask