如何在迭代时有效地替换unordered_set中的元素?

时间:2012-09-30 11:49:20

标签: c++ iterator replace unordered-set

假设你有一个

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

并且您希望在迭代它时替换它的一些元素:

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

这可能使eraseinsert上的迭代器无效(如果发生重新散列),因此此循环将显示未定义的行为,并且很可能会崩溃。

我能想到的一个解决方案是使用两个单独的vector来缓冲inserterase操作,然后使用带有迭代器对的重载进行擦除和插入(这个可能更有利于复兴。

即使我使用缓冲区方法,这仍然看起来很臃肿的代码,并且可能导致两次重访,这可能都是不必要的。

那么,有更好的方法吗?

4 个答案:

答案 0 :(得分:1)

我只想到了一种可能的方法(只是在询问之后),但也许还有更好的方法。

将所有内容复制到矢量,然后从矢量重建集合应该更快:

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());

答案 1 :(得分:1)

当您致电as.erase(it)时,迭代器it将失效。插入无序关联容器会使所有迭代器无效。因此,插入需要与迭代器分离。避免插入也是必要的,以避免处理新插入的对象:

std::vector<std::shared_ptr<A>> replaced;
for (auto it = as.begin(); it != as.end(); ) {
    if ((*it)->condition()) {
        replaced.push_back(std::make_shared<A>(**it));
        as.erase(it++);
    }
    else {
        ++it;
    }
}
std::copy(replaced.begin(), replaced.end(), std::inserter(as, as.begin());

答案 2 :(得分:0)

我将此作为对@bitmask答案的评论。为什么不将矢量用于替换元素?

std::vector<decltype(as)::value_type> buffer;
buffer.reserve(as.size());
for (auto it = as.begin(); it != as.end(); )
{
  if ((*it)->condition())
  {
    buffer.push_back(*it);
    it = as.erase(it);
  }
  else
  {
    ++it;
  }
}
as.insert(buffer.begin(),buffer.end());

而且,如果*it已经是shared_ptr<A>,我又找不到make_shared()的理由。只需分配并让复制构造函数/赋值运算符发挥其魔力。

答案 3 :(得分:-1)

在你的情况下你可以交换我的观点:

for(auto iter = as.begin(); iter != as.end(); ++iter)
{
    if(/*Check deletion condition here*/)
    {
        auto newItem = std::make_shared<A>(/*...*/);
        swap(*iter, newItem);
    }
}