在矢量中的点处具有基于范围的循环开始

时间:2014-04-04 09:50:48

标签: c++ c++11 for-loop vector iterator

我有一个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循环只是语法糖,并且真的使用了迭代器,但我喜欢我的语法糖!

4 个答案:

答案 0 :(得分:3)

(请注意下面的编辑)

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进行了测试,它确实有效。

答案 1 :(得分:1)

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

#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

答案 2 :(得分:1)

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

如果没有迭代器,你就无法做到这一点。但是你可以避免放弃用于迭代整个集合的语法糖,参见this answer。我没有测试过那段代码,但对该答案的评论表明boost :: counting_range可能更好。

答案 3 :(得分:0)

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

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

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