有没有一种方法可以迭代至多N个元素使用基于范围的循环?

我想知道是否有一个很好的方法来遍历一个容器中的最多N个元素,使用基于循环的范围和/或标准库中的algorithm(这就是整个问题,我知道我可以使用“old “为条件循环)。

基本上,我正在寻找对应于这个Python代码的东西:

for i in arr[:N]: print(i) 

正如我个人会使用这个或这个答案(两者都+1),只是为了增加你的知识 – 有助推适配器可以使用。 对于你的情况 – 切片似乎是最合适的:

 #include <boost/range/adaptor/sliced.hpp> #include <vector> #include <iostream> int main(int argc, const char* argv[]) { std::vector<int> input={1,2,3,4,5,6,7,8,9}; const int N = 4; using boost::adaptors::sliced; for (auto&& e: input | sliced(0, N)) std::cout << e << std::endl; } 

一个重要的注意事项: sliced所需要的N不大于distance(range) – 更安全(和更慢)的版本如下:

  for (auto&& e: input | sliced(0, std::min(N, input.size()))) 

所以 – 再次 – 我会使用更简单的,旧的C / C ++方法(这是你想避免在你的问题;)

这是最便宜的保存解决scheme,适用于所有我可以提出的转发迭代器:

 auto begin = std::begin(range); auto end = std::end(range); if (std::distance(begin, end) > N) end = std::next(begin,N); 

这可能会在范围内运行几乎两次,但我看不出有其他方法来获得范围的长度。

您可以使用良好的旧break来在需要时手动中断循环。 它甚至可以用基于范围的循环工作。

 #include <vector> #include <iostream> int main() { std::vector<int> a{2, 3, 4, 5, 6}; int cnt = 0; int n = 3; for (int x: a) { if (cnt++ >= n) break; std::cout << x << std::endl; } } 

C ++非常棒,因为你可以编写自己可怕的解决scheme,并将它们隐藏在抽象层之下

 #include <vector> #include <iostream> //~-~-~-~-~-~-~- abstraction begins here ~-~-~-~-~-// struct range { range(std::vector<int>& cnt) : m_container(cnt), m_end(cnt.end()) {} range& till(int N) { if (N >= m_container.size()) m_end = m_container.end(); else m_end = m_container.begin() + N; return *this; } std::vector<int>& m_container; std::vector<int>::iterator m_end; std::vector<int>::iterator begin() { return m_container.begin(); } std::vector<int>::iterator end() { return m_end; } }; //~-~-~-~-~-~-~- abstraction ends here ~-~-~-~-~-// int main() { std::vector<int> a{11, 22, 33, 44, 55}; int n = 4; range subRange(a); for ( int i : subRange.till(n) ) { std::cout << i << std::endl; // prints 11, then 22, then 33, then 44 } } 

现场示例

上面的代码显然缺less一些错误检查和其他调整,但是我只想清楚地expression这个想法。

这是因为基于范围的for循环产生类似于下面的代码

 { auto && __range = range_expression ; for (auto __begin = begin_expr, __end = end_expr; __begin != __end; ++__begin) { range_declaration = *__begin; loop_statement } } 

(CFR)。 begin_exprend_expr

如果您的容器没有(或可能没有)RandomAccessIterator,那么仍然有一种方法来处理这个猫:

 int cnt = 0; for(auto it=container.begin(); it != container.end() && cnt < N ; ++it,++cnt) { // } 

至less对我来说,这是非常可读的:-)。 无论容器types如何,它都具有O(N)复杂性。

这是一个索引迭代器。 主要是模仿,因为我很懒

 template<class T> struct indexT //: std::iterator< /* ... */ > // or do your own typedefs, or don't bother { T t = {}; indexT()=default; indexT(T tin):t(tin){} indexT& operator++(){ ++t; return *this; } indexT operator++(int){ auto tmp = *this; ++t; return tmp; } T operator*()const{return t;} bool operator==( indexT const& o )const{ return t==ot; } bool operator!=( indexT const& o )const{ return t!=ot; } // etc if you want full functionality. // The above is enough for a `for(:)` range-loop }; 

它包装一个标量typesT ,并在*返回一个副本。 它也适用于迭代器,有趣的是,这在这里很有用,因为它可以让我们有效地从一个指针inheritance:

 template<class ItA, class ItB> struct indexing_iterator:indexT<ItA> { ItB b; // TODO: add the typedefs required for an iterator here // that are going to be different than indexT<ItA>, like value_type // and reference etc. (for simple use, not needed) indexing_iterator(ItA a, ItB bin):ItA(a), b(bin) {} indexT<ItA>& a() { return *this; } indexT<ItA> const& a() const { return *this; } decltype(auto) operator*() { return b[**a()]; } decltype(auto) operator->() { return std::addressof(b[**a()]); } }; 

索引迭代器包装两个迭代器,其中第二个必须是随机访问。 它使用第一个迭代器来获取一个索引,它用来从第二个查找值。

接下来,我们有一个范围types。 一个SFINAE改进的可以find很多地方。 它使遍历for(:)循环中的一系列迭代器变得简单:

 template<class Iterator> struct range { Iterator b = {}; Iterator e = {}; Iterator begin() { return b; } Iterator end() { return e; } range(Iterator s, Iterator f):b(s),e(f) {} range(Iterator s, size_t n):b(s), e(s+n) {} range()=default; decltype(auto) operator[](size_t N) { return b[N]; } decltype(auto) operator[] (size_t N) const { return b[N]; }\ decltype(auto) front() { return *b; } decltype(auto) back() { return *std::prev(e); } bool empty() const { return begin()==end(); } size_t size() const { return end()-begin(); } }; 

以下是帮助indexT轻松实现indexT范围的indexT

 template<class T> using indexT_range = range<indexT<T>>; using index = indexT<size_t>; using index_range = range<index>; template<class C> size_t size(C&&c){return c.size();} template<class T, std::size_t N> size_t size(T(&)[N]){return N;} index_range indexes( size_t start, size_t finish ) { return {index{start},index{finish}}; } template<class C> index_range indexes( C&& c ) { return make_indexes( 0, size(c) ); } index_range intersect( index_range lhs, index_range rhs ) { if (lhs.bt > rhs.et || rhs.bt > lhs.bt) return {}; return {index{(std::max)(lhs.bt, rhs.bt)}, index{(std::min)(lhs.et, rhs.et)}}; } 

好吧,几乎在那里。

index_filter_it需要一系列索引和一个随机访问迭代器,并将一系列索引迭代器放入该随机访问迭代器的数据中:

 template<class R, class It> auto index_filter_it( R&& r, It it ) { using std::begin; using std::end; using ItA = decltype( begin(r) ); using R = range<indexing_iterator<ItA, It>>; return R{{begin(r),it}, {end(r),it}}; } 

index_filter接受一个index_range和一个随机访问容器,交叉索引,然后调用index_filter_it

 template<class C> auto index_filter( index_range r, C& c ) { r = intersect( r, indexes(c) ); using std::begin; return index_filter_it( r, begin(c) ); } 

现在我们有:

 for (auto&& i : index_filter( indexes(0,6), arr )) { } 

和中提琴,我们有一个大的乐器。

活的例子

Fancierfilter是可能的。

 size_t filter[] = {1,3,0,18,22,2,4}; using std::begin; for (auto&& i : index_filter_it( filter, begin(arr) ) ) 

将访问arrarr 。 但是,除非arr.begin()[]边界检查,否则它不会进行边界检查。

上面的代码中可能有错误,你应该使用boost

如果你在indexT上实现了-[] ,你甚至可以菊链连接这些范围。

这个解决scheme不会超过end() ,对std::list (不使用std::distance )具有O(N)复杂性,与std::for_each ,只需要ForwardIterator

 std::vector<int> vect = {1,2,3,4,5,6,7,8}; auto stop_iter = vect.begin(); const size_t stop_count = 5; if(stop_count <= vect.size()) { std::advance(stop_iter, n) } else { stop_iter = vect.end(); } std::for_each(vect.vegin(), stop_iter, [](auto val){ /* do stuff */ }); 

它唯一不能做的就是使用InputIterator比如std::istream_iterator – 你必须使用外部计数器。

首先我们写一个停止在给定索引处的迭代器:

 template<class I> class at_most_iterator : public boost::iterator_facade<at_most_iterator<I>, typename I::value_type, boost::forward_traversal_tag> { private: I it_; int index_; public: at_most_iterator(I it, int index) : it_(it), index_(index) {} at_most_iterator() {} private: friend class boost::iterator_core_access; void increment() { ++it_; ++index_; } bool equal(at_most_iterator const& other) const { return this->index_ == other.index_ || this->it_ == other.it_; } typename std::iterator_traits<I>::reference dereference() const { return *it_; } }; 

现在我们可以编写一个algorithm来使这个迭代器在给定的范围内发怒:

 template<class X> boost::iterator_range< at_most_iterator<typename X::iterator>> at_most(int i, X& xs) { typedef typename X::iterator iterator; return std::make_pair( at_most_iterator<iterator>(xs.begin(), 0), at_most_iterator<iterator>(xs.end(), i) ); } 

用法:

 int main(int argc, char** argv) { std::vector<int> xs = {1, 2, 3, 4, 5, 6, 7, 8, 9}; for(int x : at_most(5, xs)) std::cout << x << "\n"; return 0; }