Iterating over an odd (even) elements only in a range-based loop

  • A+
Category:Languages

Suppose we have a plain array (or other container which support range based loops):

const int N = 8; int arr[N] = {0, 1, 2, 3, 4, 5, 6, 7}; 

Using indexes or iterators we can loop over odd elements increasing the index by two:

for (int i = 0; i < N; i+=2) {    std::cout << arr[i] << std::endl; } 

How can I get a similar result using range based loop and avoiding explicit iterators/indexes or iteration skipping usage? Something like:

for (const auto& v: odd_only(arr)) {    std::cout << v << std::endl; } 

What the simple and elegant solution do you see? Does the Standard Library contain something like that?

 


There's no support for what you request – but you might write your own even_only and odd_only implementations.

Basic idea is to wrap around the normal iterator of the container in question and do a double increment internally each time we increment once externally:

template <typename C, bool IsOdd> class even_odd_only {     C& c; public:     class iterator     {     public:         // all the definitions required for iterator!         // most if not all might simply be derived from C::iterator...          // copy/move constructor/assignment as needed          // core of the wrapper: increment twice internally!         // just doing += 2 is dangerous, though, we might increment beyond         // the end iterator (undefined behaviour!)additionally, += 2 only         // is possible for random access iterators (so we limit usability)         void operator++() { ++b; if(b != e) ++b; }          // operator* and operator-> (both return *b), post-increment         // (defined in terms of pre-increment), etc...         // comparison: only needs to compare b iterators!      private:         C::iterator b;         C::iterator e; // needed for comparison to avoid incrementing beyond!         iterator(C::iterator b, C::iterator e) : b(b), e(e) { }     };     // const_iterator, too; possibly make a template of above     // and derive const and non-const iterators from?      even_odd_only(C& c) : c(c) { }      iterator begin()     {         using std::begin;         using std::end;         using std::empty;         auto b = begin(c);         // should be self-explanatory:         // skip first element in odd variant (if there is)         if constexpr(IsOdd) { if(!empty(c)) { ++b; } }         return iterator(b, end(c));     };     iterator end()     {         using std::end;         return iterator(end(c), end(c));     } };  template <typename T> using even_only = even_odd_base<T, false>; template <typename T> using odd_only = even_odd_base<T, true>; 

As is, it would work even with non-random-access and even non-bidirectional iterators. But especially for RA-iterators, it's less efficient than the classic loop (due to the intermediate if in operator++).

Defining comparison iterators: always operator== and operator!=, only for random access operators you can additionally have operator[<|>|<=|>=] (→ std::enable_if).

You'll find more details about how to write an iterator here – keep in mind when you encounter, though, that std::iterator itself is deprecated now.

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: