end() 返回的迭代器对于哪个标准容器(如果有)是持久的?

Posted

技术标签:

【中文标题】end() 返回的迭代器对于哪个标准容器(如果有)是持久的?【英文标题】:For which standard container, if any, is the iterator returned by end() persistent? 【发布时间】:2013-03-14 16:13:18 【问题描述】:

我需要一种方法来快速访问容器中的数据。

所以我记得那个数据位置的迭代器。之后可能会修改容器(添加和删除元素),但如果我使用不会使我的迭代器无效的容器类型(如 std::mapstd::list)我很好。

我的数据可能还没有在容器中,所以我将迭代器设置为container.end() 以反映这一点。

哪个标准容器保证end() 在添加和删除元素时不会改变?所以我仍然可以将我的迭代器与container.end() 返回的值进行比较,而不会得到假阴性。

【问题讨论】:

这是未定义的,不应依赖。正常的迭代器失效规则适用。 (您是否试图过早优化?) 相关:Iterator invalidation rules @sehe 如果应用正常的迭代器失效规则,那么它不是未定义的。正如我所提到的,我使用的容器类型不会使我的迭代器失效。 我也认为它是未定义的,但是如果你应用正常的失效规则,请注意:当你收缩一个容器时,可以认为过去的最后一个或end元素被删除了。 见this related question。显然,在 C++03 中,普通规则不适用于 end,并且 last-last 迭代器可能会失效。 【参考方案1】:

23.2.4/9 谈到关联容器:

insert 和 emplace 成员不影响 迭代器和对容器的引用,擦除成员应 仅使迭代器和对已擦除元素的引用无效

现在,有些地方标准谈到不使“迭代器和对容器元素的引用”无效,因此不包括end()。我不相信这是其中之一 - 我很确定 end() 迭代器是“容器的迭代器”。

23.3.5.4/1 对 std::listinsert “不影响迭代器和引用的有效性”,而 23.3.5.4/3 说 erase “仅使迭代器和对已擦除对象的引用无效元素”。同样,end() 迭代器是迭代器,因此不排除它们的有效性。

需要注意的一点是,对于任何容器,swap 可以使 end() 迭代器无效(我假设这是因为有两种“自然”行为,或者 end 迭代器指向相同的结尾容器或与之交换的容器的末尾,但标准不想规定或排除其他可能性)。但你不是在交换,只是添加和删除元素。

【讨论】:

我希望我在我的回答中加入了这些花哨的标准引号。也许我不会被 Zadirion 否决,哈!先生,为您 +1。【参考方案2】:

根据我的经验,来自std::vectorstd::dequeue 的迭代器在从擦除或添加调整大小时中断(这也适用于std::string 的存储)。 std::liststd::map 不分配内存块:它们通常分配单个节点(并且大多数 std::unordered_map 实现将桶作为项目的链接列表(例如,std::list))。

如果您需要一个幸存的end() 迭代器,请选择一个std::list(我这样做是为了我的信号/插槽实现,为了他们的令牌)或使用std::vector/std::dequeue 进行您自己的个人记账。

编辑: 因此,std::list 是让您的迭代器始终有效的好方法,前提是您的列表本身永远不会消亡(它们不会消亡)。从另一个答案中,如果您需要标准的清晰度:

23.3.5.4/1 表示 std::list 插入“不影响迭代器和引用的有效性”,而 23.3.5.4/3 表示擦除“仅使迭代器和对已擦除元素的引用无效”。同样, end() 迭代器是迭代器,因此不排除它们的有效性。 - Another Answer

【讨论】:

我想这就是我所说的“根据我的经验”。那好吧。我确实说你可以自己记账。 谢谢,但我想知道这是否是有保证的行为而不是观察到的。 每个人都在关注 Zadirion 的无效推荐,并否决了完全正确的答案。荒谬的 @aleguna:它如何回答 OP 的保证问题?这可能是正确的,但肯定是针对不同的问题。 @Zadirion:我认为对于标准保证来说,仅仅声明某些东西就足够了,你需要标准报价。【参考方案3】:

使用vector 代替迭代器并存储索引值。他们将在任何重组中幸存下来。迭代器主要用于指定范围的成对使用;如您所见,挂在单个迭代器上会变得很混乱。

【讨论】:

这可能效果不佳,好像发生了调整大小一样,您必须 A) 更新所有索引或 B) 只需“清空”插槽,然后获得空闲索引列表。 很遗憾,这不是一个好主意。我可能有 std::list 并保留迭代器。从中删除元素后,我的迭代器会很好。矢量中的位置将被破坏,因此这不是充分的替换。 @ThePhD - 向量索引在调整大小后仍然有效。但是,它们在删除元素后不起作用。 @Slava - 叹息,你确实说你需要删除元素。对于那个很抱歉。尽管如此,我仍然坚持我所说的(部分):不要使用迭代器。使用指针和 NULL 指针表示没有元素。 @PeteBecker 指针不起作用,因为我需要删除容器中与我的数据相关的元素。另一种方法是扫描整个容器并找到要删除的元素,但这可能太昂贵了。

以上是关于end() 返回的迭代器对于哪个标准容器(如果有)是持久的?的主要内容,如果未能解决你的问题,请参考以下文章

3.4迭代器介绍

迭代器基本

当(例如)容器大小变化时,容器 end() 迭代器如何演变[重复]

反向迭代器

如何为迭代器返回“null”值?

怎么删除STL容器的元素