在矢量中的点开始基于范围的 for 循环

Have range-based for loop start at point in vector

本文关键字:范围 循环 for 于范围 开始      更新时间:2023-10-16

我有指向某个类对象的指针std::vector<MyClass*> container,并希望使用基于范围的 for 循环遍历向量,如下所示

for (MyClass *item : container)
{
    // Do stuff, not changing the container
}

在这个循环中,我想再次遍历容器,但从容器中的下一个元素开始。如果没有迭代器,我真的找不到一种方法来做到这一点,所以我的解决方案是只使用它们

for (auto item1 = container.begin(); item1 != container.end(); ++item1)
{
    for (auto item2 = item1.next(); item2 != container.end(); ++item2)
    {
        // Do stuff, not changing the container
    }
}

我的问题是:有没有办法做到这一点,而不必求助于迭代器?我意识到基于范围的 for 循环只是语法糖,并且确实使用迭代器,但我喜欢我的语法糖!

(请注意下面的编辑)

for-range 循环不允许您访问内部迭代器。一方面,这很好,因为你不能搞砸它。另一方面,这可能很糟糕,因为您需要迭代器知道您在容器中的位置。因此,我们不能使用 for-range 循环作为外部循环,除非您使用线性内存布局迭代某些内容,您可以在其中将项目的地址作为迭代器。

此外,标准库还不能胜任以方便的方式处理范围的任务。我正在使用 Boost 的一些实用程序,例如iterator_range用于此代码片段:

using boost::make_iterator_range;
using std::next;
for (auto iter1 = container.begin(), ee = container.end(); iter1 != ee; ++iter1) {
    auto& item1 = *iter1;
    for (auto& item2 : make_iterator_range(next(iter1),ee)) {
        // do something with item1 and item2
    }
}

诚然,不是很漂亮。但这显示了如何在给定一对迭代器的情况下使用 for-range 循环的一种方法。基本上,for-range循环吃掉任何提供开始/结束功能的东西。 make_iterator_range包装一对迭代器并返回一些提供开始/结束函数的东西。

编辑:我最近了解到boost::irange也可以生成迭代器序列(而不仅仅是数字序列)。考虑到这一点,请查看此程序:

#include <iostream>
#include <vector>
#include <boost/range/irange.hpp>
#include <boost/range/iterator_range.hpp>
#include <boost/range/adaptor/indexed.hpp>
int main()
{
    using std::vector;
    using std::next;
    using boost::irange;
    using boost::make_iterator_range;
    namespace ba = boost::adaptors;
    vector<double> x {1.1, 2.2, 3.3, 4.4, 5.5, 6.6};
    for (auto iter1 : irange(begin(x),end(x))) {
        auto& item1 = *iter1;
        for (auto& item2 : make_iterator_range(next(iter1),end(x))) {
            // do something with item1 and item2
            std::cout << item1 << " < " << item2 << std::endl;
        }
    }
    return 0;
}

我在 C++0x 模式下使用 G++ 4.6.3 和 Boost 1.48 对此进行了测试,它确实有效。

根据 OP 的请求添加了我的评论作为答案。

没有迭代器,就无法做到这一点。但是你可以避免放弃迭代整个集合的语法糖,请参阅此答案。我还没有测试过该代码,但对该答案的评论建议 boost::counting_range,这可能会更好。

您可以创建一个将访问该范围的简单类:

#include <vector>
#include <iostream>
template<typename I>
struct Sub {
    I _begin;
    I _end;
    Sub(I b, I e): _begin(b), _end(e) {}
    I begin() const { return _begin; }
    I end() const { return _end; }
};
template<typename I>
Sub<I> sub(I b, I e) { return Sub<I>(b, e); }
int main() {
    std::vector<int> a{0,1,2,3,4,5,6,7,8,9};
    for (auto i: sub(a.begin() + 3, a.end() - 1)) {
        std::cout << i << "n";
    }
}

此代码打印:

3
4
5
6
7
8

循环的范围迭代容器中的所有元素。这意味着您可以将该循环更改为:

for (auto item1 : container )
{
    for (auto item2 = item1.next(); item2 != container.end(); ++item2)
    {
        // Do stuff, not changing the container
    }
}

由于内部循环不会迭代整个容器,因此必须使用 normal for 循环。