循环中的C ++映射擦除元素是否会使迭代器无效? (C ++ 03)

时间:2017-05-05 02:32:33

标签: c++ dictionary iterator c++03 erase

我有这段代码:

#include <cstdlib>
#include <map>
#include <string>
#include <iostream>
#include <algorithm>
#include <stdint.h>

class Table
{
public:
  std::map<uint32_t, std::string> m_map;

  typedef std::map<uint32_t, std::string>::iterator Iterator_t;
  typedef std::map<uint32_t, std::string>::const_iterator ConstIterator_t;

  Table () : m_map () { }

  void
  Erase (Iterator_t entry_it, const std::string & reason)
  {
    std::cout << reason << " " << entry_it->first << ":" << entry_it->second << "\n";
    m_map.erase (entry_it);
//        m_map.erase (entry_it->first);
  }

  bool
  Insert (const uint32_t & key, const std::string & value)
  {
    ConstIterator_t found_it = m_map.find (key);

    if (found_it != m_map.end ())
      {
        std::cout << "Key [" << key << "] already exists\n";
        return false;
      }

    m_map.insert (std::make_pair (key, value));
    std::cout << key << ":" << value << " inserted\n";
    return true;
  }

  void
  Print () const
  {
    std::cout << "Table: ";
    for (ConstIterator_t it = m_map.begin (); it != m_map.end (); ++it)
      {
        std::cout << it->first << ":" << it->second << " ";
      }
    std::cout << "\n";
  }

  void
  Purge ()
  {
    for (Iterator_t it = m_map.begin (); it != m_map.end (); ++it)
      {
        if (it->second.length () <= 4)
          {
            Erase (it, "Erase entry ");
          }
      }
  }
};

int
main (int argc, char** argv)
{
  Table table;

  table.Insert (1, "one");
  table.Insert (2, "two");
  table.Insert (3, "three");
  table.Insert (4, "four");
  table.Insert (5, "nine");
  table.Insert (6, "six");
  table.Insert (7, "seven");
  table.Insert (8, "eight");
  table.Insert (9, "nine");
  table.Print ();
  std::cout << "\n";

  table.Purge ();
  table.Print ();
  std::cout << "\n";

  return 0;
}

产生以下输出:

1:one inserted
2:two inserted
3:three inserted
4:four inserted
5:five inserted
6:six inserted
7:seven inserted
8:eight inserted
9:nine inserted
Table: 1:one 2:two 3:three 4:four 5:nine 6:six 7:seven 8:eight 9:nine 

Erase entry  1:one
Erase entry  2:two
Erase entry  4:four
Erase entry  6:six
Erase entry  9:nine
Table: 3:three 5:five 7:seven 8:eight 

正如您所见,5:five应该已被删除,但事实并非如此。我不完全理解为什么。

我试过的m_map.erase ()的两个重载产生了相同的结果。而我无法找到地图的std::remove_if版本。

我必须指出,必须在C ++ 03 中,而我只找到了C ++ 11/14的答案。你能告诉我怎么解决这个问题吗?

2 个答案:

答案 0 :(得分:4)

是的,这是未定义的行为。

erase()使删除的映射值的迭代器无效。然后循环尝试递增无效的迭代器。如果在循环内直接调用erase(),或者在循环调用的函数中调用,则无关紧要。无论哪种方式,这都是未定义的行为。对于C ++ 03,C ++ 11和C ++ 14都是如此。从根本上说,这是地图的工作方式。

答案 1 :(得分:2)

当@sam回答时,std::map::erase将使擦除元素的迭代器无效。之后,无效迭代器用于for循环中的递增操作,这是未定义的行为。

  

擦除元素的引用和迭代器无效。

要解决它,从C ++ 11开始,您可以使用erase的返回值,它是删除元素后面的迭代器。但是对于C ++ 03,你必须手动完成。 e.g。

class Table
{
public:
  ...    
  void
  Purge ()
  {
    for (Iterator_t it = m_map.begin (); it != m_map.end (); )
      {
        if (it->second.length () <= 4)
          {
            Erase (it++, "Erase entry ");
          }
        else
          {
            ++it;
          }
      }
  }
};

上述代码的要点是在erase发生之前执行增量(而it仍然是有效的迭代器)。我们利用it++将返回it的原始值的事实。因此,评估顺序为:(1)it递增; (2)it的原始值传递给erase; (3)元素是erase d。