如何处理多个迭代器类型

时间:2011-12-16 14:45:02

标签: c++ data-structures stl iterator

我有一个自定义数据结构,可以通过多种方式访问​​。我想尽可能保持这个数据结构符合STL标准。所以我已经有很多typedef,它给模板参数提供STL名称。现在这对我来说一切照旧。

但是我不太确定如何正确地将迭代器添加到我的数据结构中。我面临的主要问题是,数据结构上会有多个迭代策略。最简单的用例是遍历所有元素,这些元素可以通过数据结构上的STL-Conforming迭代器很好地处理。然而,人们可能还想访问元素,这些元素在某种程度上类似于给定的键。我还想以一种可以与STL接口的方式迭代所有这些相似的元素。

这些是我到目前为止所考虑的想法:

  • 只提供一种类型的迭代器

这基本上是std::map的作用。子范围的开始和结束迭代器由std::map::lower_bound()std::map::upper_bound()提供。

但是这很有效,因为begin()end()lower_bound()upper_bound()返回的迭代器是兼容的,即operator==()可以给出一个在这些上有非常明确的含义。在我看来,这很难做到,或者甚至不可能给出一些明确的语义。例如,我可能会遇到it1==it2++it1!=++it2的情况。我不确定STL是否允许这样做。

  • 提供多种类型的迭代器

提供干净的operator==()语义要容易得多。另一方面令人讨厌,因为它扩大了类型的数量。

  • 提供一种类型的迭代器并使用stl :: algorithms进行专门访问

我不确定这是否可行。迭代状态应该由迭代器以某种方式保存(直接或在Memento中)。使用这种方法意味着专门化所有stl :: algorithms并直接在特化中访问谓词。很可能是不可能的,如果可能的话,也是一个非常糟糕的主意。

现在我主要选择版本1,即根本不提供一种类型的迭代器。但是,由于我不清楚如何清理语义,我还没有决定。

你会如何处理?

2 个答案:

答案 0 :(得分:3)

为什么更多类型出现问题?它并不一定意味着更多的代码。例如,您可以使迭代器键入一个模板,该模板将迭代策略作为模板参数。然后,迭代策略可以提供迭代的实现:

struct iterate_all_policy {
    iterate_all_policy(iterator<iterate_all_policy> & it) : it(it) {}

    void advance() { /* implement specific advance here */ }
private:
    iterator<iterate_all_policy> & it;
}

您可能必须使iterator-types的迭代策略类成为朋友。

答案 1 :(得分:2)

标准容器支持两种迭代策略,包含两种迭代器类型:::iterator::reverse_iterator。您可以使用std::reverse_iterator的构造函数及其成员函数base()在两者之间进行转换。

根据迭代策略的相似程度,向不同的迭代器类型提供转换可能也可能不容易。我们的想法是结果应该指向目标类型的迭代策略中的“等效位置”。对于反向迭代器,这个等价是通过说如果在该点插入,结果是相同的来定义的。因此,如果rit是反向迭代器,vec.insert(rit.base(), ...)在反向迭代中“rit 之前插入一个元素,也就是说之后 em>容器中rit指向的元素。这非常繁琐,只有在迭代策略完全不相关时才会变得更糟。但是如果你的所有迭代器类型都是(或者可以看起来像)围绕所有元素的“普通”迭代器的包装器,那么你可以根据底层迭代器位置定义转换。

如果有成员函数添加或删除容器的元素,那么实际上只需要需要转换,因为您可能不希望为每个迭代器类型提供单独的重载(就像标准容器没有为反向迭代器定义inserterase。如果迭代器仅用于指向元素,那么很可能没有它们。

如果不同的迭代策略都是按照正常顺序在元素的子集上进行迭代,那么请查看boost::filter_iterator

  

我可能会遇到it1==it2++it1!=++it2的情况。我是   不确定STL是否允许这样做。

如果我理解正确,您从it1开始thing.normal_begin(),从it2开始获得thing.other_policy_begin()。标准容器没有定义比较属于不同范围的相同类型的迭代器的结果,所以如果你确实使用了一个普通类型,那么我认为这样就好了,前提是文档清楚地说明了{{1}确实有效,范围根据迭代器的来源分开。

例如,您可以使用operator==作为构造函数参数,每次调用skip_iterator时它应该向前移动的步数。然后,您可以在比较中包含该整数,以便++,或者您可以将其排除在thing.skip_begin(1) != thing.skip_begin(2)thing.skip_begin(1) == thing.skip_begin(2)之外。我认为如果有文件记载,或者你可以证明比较它们是UB,除非它们来自同一个起点。