简体   繁体   中英

C++ iterator over certain elements in vector

Please forgive me if this is a trivial question, I'm just learning C++ and try to wrap my head around certain concepts. Especially when it comes to iterators, I'm completely lost.

Say I have a custom class representing some data structure, and one of its members is a vector of integers. I want to write a bidirectional iterator for that class, which outputs only the EVEN numbers in the vector. Is there an easy and instructive way? I'd prefer not using libraries other than STL.

Not sure making your own iterator is easy. But probably the best is to use a conditional for_each functions.

std::for_each does an operation on every elements. It's pretty easy to create a for_each_if doing an operation on some specific elements. For instance, program below only prints even numbers from the vector (4, 6 and 8).

#include <iostream>
#include <vector>

using namespace std;

struct is_even {
  typedef bool return_type;
  bool operator() (const int& value) {return (value%2)==0; }
};

struct doprint {
  bool operator() (const int& value) { std::cout << value << std::endl; }
};

template <class InputIterator, class Predicate, class Function> 
void for_each_if(InputIterator first, InputIterator last, Function f, Predicate pred) 
{ 
    while ( first != last )
    {  
        if (pred (*first)) 
            f(*first++);  
        else 
            first ++; 
    }
}

int main()
{
   std::vector<int> v;
   v.push_back( 4 );
   v.push_back( 5 );
   v.push_back( 6 );
   v.push_back( 8 );

   for_each_if( v.begin(), v.end(), doprint(), is_even());

   return 0;
}

Going by your requirements, deriving from vector::iterator would probably be the easiest:

class my_iterator : private std::vector<int>::iterator {
    typedef std::vector<int>::iterator base;
    base m_base_end;   // Stores the real end iterator of the internal vector.
                       // We need it so that the even/odd checking code
                       // doesn't run off the end of the vector
                       // (initialize it in a constructor, omitted here for
                       // brevity).
public:

    using base::operator*;
    using base::operator->;
    // etc. for other members...

    // except for increment:

    my_iterator& operator++()
    {
        do {
            base::operator++();
        } while( static_cast<base&>(*this) != m_base_end
                 && base::operator*() % 2 );
        return *this;
    }

    my_iterator operator++(int)
    {
        my_iterator temp;
        operator++();
        return temp;
    }

    // TODO: decrement and other arithmetic operators
};

It's still quite a bit of boilerplate, and you'll also want to do the same for const_iterator (I'd probably make the above class a template to make it easier).

Consider Boost for this - it has filter_iterator just for this purpose. There's also iterator_adaptor if that doesn't suit you.


#include <vector>
#include <iostream>

class X {

public:

  class EvenIterator {
  public:
    EvenIterator(std::vector<int>::iterator it, std::vector&ltint>::iterator end) : it(it), end(end) {
      while (true) {
        if (isEven(*it)) {
          break;
        } else if (it == end) {
          break;
        }
        it++;
      }
    }

    bool operator != (const EvenIterator& evenIt) {
      return evenIt.it != this->it;
    }

    int operator * () {
      return *it;
    }

    EvenIterator operator ++ () {
      while (true) {
        it++;
        if (isEven(*it)) {
          return EvenIterator(it, end);
        } else if (it == end) {
          return EvenIterator(it, end);
        }
      }
    }
  private:
    std::vector&ltint>::iterator it;    
    std::vector&ltint>::iterator end;    
  };

  static bool isEven(int number) {
    return number % 2 == 0;
  }

  void add(int number) {
    v.push_back(number);
  }

  EvenIterator evenBegin() {
    return EvenIterator(v.begin(), v.end());
  }

  EvenIterator evenEnd() {
    return EvenIterator(v.end(), v.end());
  }

private:
  std::vector&ltint> v;

};

int main() {
  X x;
  x.add(1);
  x.add(2);
  x.add(3);
  x.add(2);
  x.add(2);
  x.add(31);
  x.add(56);
  x.add(101);

  for (X::EvenIterator it = x.evenBegin(); it != x.evenEnd(); ++it){
    std::cout << *it << std::endl; // only prints the even numbers
  }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM