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

前端 未结 4 1282
无人共我
无人共我 2021-02-15 16:23

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

Usi

4条回答
  •  被撕碎了的回忆
    2021-02-15 17:18

    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 
    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 
    using even_only = even_odd_base;
    template 
    using odd_only = even_odd_base;
    

    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.

提交回复
热议问题