11

I have a std::vector<std::string> v; (initialized). How can I use the range-for loop for accessing all elements except the first one (on index zero). For all elements:

for (const string & s: v) process(s); 

Instead of the v a range expression can be used. How can I write the range expression to skip the first element (or skip the first n elements)?

I know how to get the effect using v.begin() + 1 and using the classic loop. I am searching for the new, more readable, recommended alternative to do that. Possibly something similar to Python slicing? ...like:

for s in v[1:]: process(s) 
4
  • One way is to make a new vector with the required elements, and then loop through. Or you can use std::for_each: en.cppreference.com/w/cpp/algorithm/for_each Commented Aug 11, 2015 at 8:31
  • 3
    You can't. The range-based loop isn't the solution for each and every problem. Just use an ordinary loop. Commented Aug 11, 2015 at 8:38
  • As mentioned by @GargAnkit you can make a new vector with required elements. You can do this as part of the loop declaration using the vector constructor. So for (auto&& i : std::vector<std::string>(v.begin()+1,v.end())). Demo here. Not as neat as the python example you showed though. Commented Aug 11, 2015 at 8:39
  • See stackoverflow.com/questions/25652505/… which has nice templates for the general problem. Commented Sep 28, 2017 at 7:28

4 Answers 4

9

Until ranges make it into the standard library, you won't get any better than a vanilla for loop in plain C++ :

for(auto i = begin(v) + 1, e = end(v); i !=e; ++i) // Do something with *i 
Sign up to request clarification or add additional context in comments.

8 Comments

@CashCow as the question is about bending a range-for into shape, I'm assuming plain C++. I've seen enough Boost to know that nothing is impossible if you throw enough templates at it ;)
@pepr I'll be back when C++ has ranges. *evil laugh*
@Quentin: Well, you should not chew leek as on your icon (unless you are really that blue). It makes you too evilish (as some vegetarians are) ;) By the way, I like your ++i at the example. Many programmers would write i++.
@pepr High-grade leek-chewing blue... uh... things, know how to increment their iterators !
Yes, I am confused :))))
|
7

Create a wrapper for which begin() and end() return the correct iterators and then you can use that as the second argument.

#include <iostream> #include <vector> template< typename Collection > class FromNth { Collection& coll_; size_t offset_; public: FromNth( Collection& coll, size_t offset ) : coll_( coll ), offset_( offset ) { } // will nicely resolve to const_iterator if necessary auto begin() const -> decltype( coll_.begin() ) { return coll_.begin() + offset_; } auto end() const -> decltype( coll_.end() ) { return coll_.end(); } }; template< typename Collection > FromNth<Collection> makeFromNth( Collection& collection, size_t offset ) { return FromNth<Collection>( collection, offset ); } template< typename Collection > auto begin( const FromNth<Collection> & wrapper ) -> decltype( wrapper.begin() ) { return wrapper.begin(); } template< typename Collection > auto end( const FromNth<Collection> & wrapper ) -> decltype( wrapper.end() ) { return wrapper.end(); } int main() { std::vector< int > coll { 2, 3, 5, 7, 11, 13, 17, 19, 23 }; for( auto x : makeFromNth( coll, 1 ) ) { std::cout << x << '\n'; } return 0; } 

Note that my fromNth "begin" is undefined behaviour if the size of the input is less than the offset. (If it's equal then it's well defined and begin == end). Therefore do a size check first.

Note: if you are using a recent enough version of boost then iterator_range may already provide you such a "collection" that is similar to my "FromNth".

for( auto const& s : boost::make_iterator_range( v.begin() + 1, v.end() ) ) { process( s ); } 

Note: the code above worked on CodingGround using C++11 GNU 4.8.3. (That site is very slow though). From C++14 you will not need the ->decltype statements (which are needed in C++11 for templates).

Output:

sh-4.3$ g++ -std=c++11 -o main *.cpp sh-4.3$ main 3 5 7 11 13 17 19 23 

1 Comment

Thanks for the information! I was expecting that there is some std code like this, already (and I could not find) ;)
4

C++20 comes with std::ranges::drop_view to achieve this:

// iterate through elements of v skipping the first for (const auto& s : std::ranges::drop_view{v,1}) { process(s); } 

Comments

1

You could use Eric Niebler's Range V3 library, and the tail view, that just creates a view of the original container without the first element.

#include <range/v3/view/tail.hpp> for (const auto& s : ranges::views::tail(v)) { process(s); } 

2 Comments

This is very old question. These days, I would probably prefer the std::ranges::drop_view. Anyway, thank you for the information.
@pepr I do also prefer the std when possible. But, for example, if you're using GitHub Actions nowadays, you'll find that STL ranges are not implemented everywhere. I think they were included in Clang 15, and GitHub is still using Clang 14.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.