Have range-based for loop start at point in vector

戏子无情 提交于 2020-07-23 01:00:45

问题


I have a std::vector<MyClass*> container of pointers to objects of some class, and want to iterate over the vector using a range-based for loop, like this

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

Inside this loop I want to loop through the container one more time, but starting at the next element in the container. I couldn't really find a way of doing this without iterators, so my solution was to just use those instead

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

My question is: is there any way of doing this, without having to resort to iterators? I realize range-based for loops are just syntactical sugar, and really uses iterators, but I like my syntactical sugar!


回答1:


Added my comment as an answer by request from OP.

You can't do that without iterators. But you can avoid giving up the syntactic sugar for iterating the entire collection, see this answer. I haven't tested that code, but a comment to that answer suggests boost::counting_range, which might be even better.




回答2:


(Please note the edit below)

The for-range loop does not give you access to the internal iterator. On one hand, this is good because you can't mess with it. On the other hand this might be bad because you need the iterator to know where you are in the container. So, we can't use a for-range loop as outer loop unless you iterate over something with a linear memory layout where you can take the address of an item as an iterator.

Also, the standard library is not yet up to the task of dealing with ranges in a convenient way. I'm using some utilities from Boost like iterator_range for this code snippet:

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
    }
}

Admittedly, not very pretty. But this shows one way how you can use the for-range loop given a pair of iterators. Basically, the for-range loop eats anything that offers begin/end functions. make_iterator_range wraps a pair of iterators and returns something, that provides begin/end functions.

Edit: I recently learned that boost::irange can also produce sequences of iterators (and not just sequences of numbers). With that in mind, check out this program:

#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;
}

I tested this with G++ 4.6.3 in C++0x mode and Boost 1.48 and it actually works.




回答3:


You could create a simple class that will access the 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";
    }
}

This code prints:

3
4
5
6
7
8



回答4:


The range for loop iterates all elements in the container. That means that you can change that loop to :

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

Since the inner loop doesn't iterate whole container, you have to use normal for loop.



来源:https://stackoverflow.com/questions/22859348/have-range-based-for-loop-start-at-point-in-vector

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!