在C++ STL映射上通过迭代器擦除

Com*_* 10 3 c++ iterator stl map erase

我很好奇以下代码背后的基本原理.对于给定的地图,我可以end()使用以下代码删除最多但不包括(显然)的范围:

map<string, int> myMap;
myMap["one"] = 1;
myMap["two"] = 2;
myMap["three"] = 3;

map<string, int>::iterator it = myMap.find("two");

myMap.erase( it, myMap.end() );
Run Code Online (Sandbox Code Playgroud)

这将使用范围删除最后两个项目.但是,如果我使用了擦除的单个迭代器版本,我一半期望传递myMap.end()到没有动作,因为迭代器显然在集合的末尾.这与腐败或无效的迭代器不同,后者显然会导致未定义的行为.

但是,当我这样做时:

myMap.erase( myMap.end() );
Run Code Online (Sandbox Code Playgroud)

我只是得到一个分段错误.我不会认为map很难检查迭代器是否等于end()并且在这种情况下不采取行动.这是否有一些微妙的原因让我失踪?我注意到即便如此:

myMap.erase( myMap.end(), myMap.end() );
Run Code Online (Sandbox Code Playgroud)

(即什么都不做)

我问的原因是我有一些代码接收到集合的有效迭代器(但可能是end()),我想简单地将其传递给擦除而不是像这样先检查:

if ( it != myMap.end() )
    myMap.erase( it );
Run Code Online (Sandbox Code Playgroud)

这对我来说似乎有点笨拙.另一种方法是重新编码,这样我就可以使用按键式擦除重载,但如果我能帮助它,我宁愿不重写太多.

Dav*_*eas 5

关键是在标准库中,由两个迭代器确定的范围是半开的范围.在数学符号中[a,b)它们包括第一个但不是最后一个迭代器(如果它们都相同,则范围为空).同时,end()返回一个超出最后一个元素的迭代器,它与半开范围表示法完全匹配.

当您使用它的范围版本时,erase它永远不会尝试删除最后一个迭代器引用的元素.考虑一个修改的例子:

map<int,int> m;
for (int i = 0; i < 5; ++i)
   m[i] = i;
m.erase( m.find(1), m.find(4) );
Run Code Online (Sandbox Code Playgroud)

在执行结束时,地图将保持两个键04.请注意,第二个迭代器引用的元素从容器中删除.

另一方面,单个迭代器操作将擦除迭代器引用的元素.如果上面的代码更改为:

for (int i = 1; i <= 4; ++i ) 
   m.erase( m.find(i) );
Run Code Online (Sandbox Code Playgroud)

带密钥的元素4将被删除.在您的情况下,您将尝试删除不引用有效对象的结束迭代器.

我不会认为map检查迭代器是否等于end()并且在这种情况下不采取行动.

不,这并不难,但是函数的设计考虑了不同的契约:调用者必须将迭代器传递给容器中的元素.造成这种情况的部分原因是,在C++中,大多数功能都经过精心设计,以便尽可能降低成本,使用户能够平衡自身的安全/性能.用户可以在调用之前测试迭代器erase,但如果该测试在库内,则当用户知道迭代器有效时,将无法选择退出测试.