【发布时间】:2012-09-14 12:13:08
【问题描述】:
我很好奇以下代码背后的基本原理。对于给定的地图,我可以使用以下代码删除最多但不包括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() );
这会删除使用范围的最后两项。但是,如果我使用单一迭代器版本的擦除,我有一半希望通过 myMap.end() 不会导致任何操作,因为迭代器显然位于集合的末尾。这与明显会导致未定义行为的损坏或无效迭代器不同。
但是,当我这样做时:
myMap.erase( myMap.end() );
我只是遇到了分段错误。我不会认为 map 很难检查迭代器是否等于 end() 并且在这种情况下不采取行动。我错过了一些微妙的原因吗?我注意到即使这样也有效:
myMap.erase( myMap.end(), myMap.end() );
(即什么都不做)
我问的原因是我有一些代码接收到集合的有效迭代器(但可能是end()),我想简单地将它传递给擦除,而不是像这样首先检查:
if ( it != myMap.end() )
myMap.erase( it );
这对我来说似乎有点笨拙。另一种方法是重新编码,这样我就可以使用按键类型擦除重载,但如果我能提供帮助,我宁愿不要重写太多。
【问题讨论】:
-
@JoachimPileborg:谢谢,这是合乎逻辑的。由于这个原因,传递一个有效的 - 非
end()- 迭代器两次导致没有擦除。单迭代器版本仍然让我感到困惑。 -
单迭代器版本需要一个可解引用的迭代器,即实际引用一个可以擦除的元素的迭代器。过去的迭代器是不可取消引用的,所以如果你试图擦除那个不存在的元素,你会得到未定义的行为。
-
@MikeSeymour:谢谢。是否有特定原因必须取消引用?另外,你能告诉我你在哪里找到的吗?我看到的所有在线参考资料都没有提到这个事实。
-
一般来说,
erase的实现可能很昂贵或不可能判断任何特定的迭代器是否可解除引用,因此将负担放在调用者身上。它在 C++11 标准表 100 中指定;它定义了a.erase(q)的行为,其中前一段 23.2.3/3 指定“q表示对a的有效可解引用常量迭代器”。 -
虽然,测试一个迭代器一般是否可解引用是一个比测试它是否等于
end()更难的问题。对于大多数(所有?)容器,结束迭代器是唯一不可取消引用的有效迭代器,困难的部分是跟踪迭代器是否有效。所以提问者的提议比一般的检查迭代器的负担要小。
标签: c++ stl map iterator erase