【问题标题】:Writing an iterator that makes several containers look as one编写一个使多个容器看起来像一个的迭代器
【发布时间】:2011-10-08 13:05:42
【问题描述】:

考虑以下简化示例和所需的输出:

class A
{
    class combined_iterator
    {
        ????
    }
    typedef ??? t_combined_it;

    t_combined_it begin();
    t_combined_it end();

    std::vector<int> m_Vec1, m_Vect2;
}

A a;
a.m_Vec1.push_back(1);
a.m_Vec2.push_back(2);
for (A::t_combined_it it = a.begin() ; it != a.end() ; it++) {
     std::cout << *it << " ";
}

输出:

1 2 

我认为这个问题很清楚:我如何编写一个迭代器,使它看起来好像两个或多个其他迭代器实际上只是一个序列。因此,在示例中,我可以使用一个迭代器,它首先迭代 m_Vec1 的元素,然后是 m_Vec2 的元素,而不是同时迭代 m_Vec1 和 m_Vec2。

我发现了以下问题,我认为它提出了相同的问题:Make a c++ iterator that traverses 2 containers。这个问题没有好的答案;原始提问者提出的解决方案似乎很复杂,而且(相对)占用大量内存。

我尝试了一种简单的方法,将 std::vector::iterator 保留为我的自定义迭代器的成员,并将其与被迭代的每个序列的 .end() 迭代器进行比较;然而,比较来自不同容器的迭代器似乎是非法的(我希望它们只是返回“不相等” - 也许这是寻找解决这个问题的方向?我想不出如何不过要实现它)。

在可能且相关的情况下,我想使用 boost::iterators,就像我在其他地方使用它们一样,我喜欢它为我的迭代器实现提供的同质性;但当然,如果有人有想法但不使用它们,我可以自己实现它们,所以从这个意义上说,它们不是必需的。

【问题讨论】:

    标签: c++ boost iterator


    【解决方案1】:

    其他人给出的答案仅适用于连接两个范围,而不适用于任意数量的范围。

    PStade Oven has a concatenated adaptor 用于加入 任何 个范围;它还有一个jointed 适配器,专门用于连接 2 个范围。

    如果需要,您当然可以使用begin()end() 将范围转换为迭代器;只需确保范围比迭代器更持久。

    【讨论】:

      【解决方案2】:

      boost::join 是您正在寻找的。您还可以研究实现,尤其是如何推导容器遍历、引用和返回值类型的最小公分母。引用:

      join 函数的目的是将两个范围合并为一个较长的范围。

      生成的范围将具有作为参数提供的两个范围中最低的公共遍历。

      请注意,由于需要在遍历期间检查是否已在内部到达范围的末尾,因此连接的范围会产生性能成本。

      【讨论】:

      • 谢谢,正是我想要的。我从来没有想过这会出现在 boost::range 中,尽管事后看来这是有道理的。
      【解决方案3】:

      我已经为不同的问题实现了类似的东西。实现是here。基本思想是您接近的一个:存储两个迭代器范围,当您被要求执行操作时,检查您是否已完成第一个范围内的迭代,并使用任一范围。

      这无论如何都不是线程安全的,而且我从未在实际代码中使用过该实现,因此可能存在问题和错误......

      【讨论】:

      • 谢谢,我认为这会起作用,它看起来像我所谓的“天真”解决方案,但有了 Aasmund 提出的改变。 boost::range::join 使用了一个稍微不同的方法和一个标志,但我不太清楚为什么。
      【解决方案4】:

      阿斯蒙德是对的。您的方法虽然简单,但应该可行。但是还有很大的优化空间。

      集合对象通常很大,因此需要一些时间来迭代。特别是列表和数组。

      您应该考虑使用多线程,以便可以并行迭代每个集合。

      另外。您应该听取您链接的帖子的建议使用和使用

      Boost.MultiIndex

      【讨论】:

        【解决方案5】:

        zip_iterator 如果您想并行迭代两个迭代器,则可以使用。如果您需要按顺序迭代容器,您可以使用iterator_adaptor 实现一个。

        【讨论】:

        • zip_iterator 仅在同时遍历两者时才有效,这不是我想要做的。我想有一种方法可以使用 iterator_adaptor,我的问题是 如何 :)
        【解决方案6】:

        我认为您的“幼稚”方法应该可行,但需要进行以下更改:不要将迭代器与每个容器的 end() 进行比较,而是保留一个指向 current 容器的指针并比较迭代器只有当前容器的end()。到达终点后,移动到下一个容器。这样,您将永远不会将迭代器与另一个容器进行比较,而不是它指向的容器。这也很容易推广到任意大小的集合。

        【讨论】:

        • 并在 operator== 等中首先测试每个迭代器的当前容器。
        • 当我尝试这个时,我没有保留对容器的引用,而是保留了两个范围的开始和结束迭代器。唯一的优点是它更灵活(即您可以绑定每个容器的子序列)
        • @Martin:David 的意思是将开始/结束迭代器保留在组合迭代器中,这不会影响迭代器的使用——也许我误解了你的话。跨度>
        • @Aasmund:谢谢,是的,我想这是“修复”我提出的方法的方法,我回家后会在测试代码上检查它,即使提升: :range::join 已经做了我想做的事——我仍然想知道为什么我认为这行不通,因为我认为我也尝试过类似的事情......
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-07-24
        • 1970-01-01
        • 2010-10-28
        相关资源
        最近更新 更多