【问题标题】:Removing elements from a C++ map through a for-loop通过 for 循环从 C++ 映射中删除元素
【发布时间】:2011-07-08 16:16:32
【问题描述】:

我的 STL 有点生疏,所以请原谅我问了一个可能微不足道的问题。考虑以下代码:

map<int,int> m;
...
for (auto itr = m.begin(); itr != m.end(); ++itr) {
    if (itr->second == 0) {
        m.erase(itr);
    }
}

问题是:在地图上循环时擦除元素是否安全?

【问题讨论】:

  • 再想一想,我可能应该使用remove_if 之类的东西。但是,我仍然对问题的答案感兴趣。
  • 我不明白 - 为什么你需要一个循环呢?一个映射只能包含一个值为 0 的键。因此,如果找到了 find,则该迭代器的 erase 应该可以解决问题!
  • 尼姆,我的例子有点愚蠢。一个更好的例子是if (itr-&gt;second == 0)
  • @Ben,啊——现在不一样了! ;)
  • @Ben:你不应该在排序范围上使用remove_if,它通过重新排列元素违反了“排序”假设。

标签: c++ map stl c++11


【解决方案1】:

是的,但不是你这样做的方式。您在擦除时使 itr 无效,然后递增无效的迭代器。

auto itr = m.begin();
while (itr != m.end()) {
  if (itr->first == 0) {
    m.erase(itr++);
  } else {
    ++itr;
  }
}

【讨论】:

  • 自动 tmp = ++itr; m.erase(iter); itr = tmp; // 这会更不稳定(IMO)。
  • 这也是错误的,你在擦除之前就在增加......你是否打算例如auto tmp=itr; ++itr; m.erase(tmp); ?
  • @Ben, @Erik:警告,标准中有两种erase 方法,用于基于节点的容器(listmapset,.. .) 使用此方法,对于erase 方法返回迭代器的容器(如vector),erase 行应为itr = m.erase(itr)。这个签名怪癖有效地对整个“可互换性”事物造成了影响……因此在 C++0x 中得到了纠正,因此所有没有返回任何内容的 erase 现在都将迭代器返回到下一个元素:)
  • 就我自己的理解,因为我经常使用这种模式,你能确认这意味着m.erase(itr++); 行可以成为 C++0x 中所有容器的itr = m.erase(itr++); 吗?
  • @dlanod 失去了++itr = m.erase(itr); 应该是这样,参见例如en.cppreference.com/w/cpp/container/map/erase
【解决方案2】:

我认为您根本不应该使用已删除的迭代器 - 如果列表会导致严重问题,那么地图也不应该有所不同。

编辑,作者:Matthieu M:此代码在 C++0x 中格式正确,并且允许作为 MSVC 的扩展。

map<int,int> m;
...
auto itr = m.begin();
while (itr != m.end())
{
    if (itr->second == 0) {
        itr = m.erase(itr);
    }
    else 
    {
        itr++;
    }
}

【讨论】:

  • 标准 c++ map::erase not 返回一个新的迭代器,这是一个 MSVC 扩展。
  • 此代码格式错误。没有返回 iterator 的擦除函数版本。 Visual C++ 标准库实现提供了这样的返回类型,但特别指出它不符合 C++ 标准
  • @Xader:我已恢复您的编辑并添加了免责声明,此代码在 C++0x 中格式正确,是在容器,所以给出了最好的答案。 (免责声明是为了让不理解auto 表示C++0x 的人不要混淆)。反对者的标准参考:n3225 23.5.1.2 元素访问。
  • @Matthieu:我同意。由于 OP 在他的问题中写了“C++”,我假设 auto 只是在某种意义上使用“这种类型在 C++03 中写出来的时间太长了,所以我会借用C++0x 类型说明符 auto,但该示例应被视为 C++03" 或类似的;)
  • 谢谢,我正在使用 C++0x(正如 auto 关键字和 c++0x 标签所暗示的那样),所以看起来是一个不错的解决方案。
【解决方案3】:

对于给出的示例,使用将键作为参数的 erase 重载实际上会更容易。此函数使用给定键擦除地图中的所有元素(对于地图,这始终是零或一个元素)

map<int,int> m; 
// ...
m.erase(0); // erase all elements with key equivalent to 0

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-02-02
    • 1970-01-01
    • 1970-01-01
    • 2019-11-13
    • 1970-01-01
    • 2013-05-29
    • 2021-10-12
    • 1970-01-01
    相关资源
    最近更新 更多