tags:

views:

707

answers:

5

I'd like an iterator in C++ that can only iterate over elements of a specific type. In the following example, I want to iterate only on elements that are SubType instances.

vector<Type*> the_vector;
the_vector.push_back(new Type(1));
the_vector.push_back(new SubType(2)); //SubType derives from Type
the_vector.push_back(new Type(3));
the_vector.push_back(new SubType(4)); 

vector<Type*>::iterator the_iterator; //***This line needs to change***

the_iterator = the_vector.begin();
while( the_iterator != the_vector.end() ) {
    SubType* item = (SubType*)*the_iterator;
    //only SubType(2) and SubType(4) should be in this loop.
    ++the_iterator;
}

How would I create this iterator in C++?

+2  A: 

As paintballbob said in a comment, you should create your own iterator class, perhaps inheriting from vector<Type*>::iterator. In particular, you will need to implement or override operator++() and operator++(int) to ensure that you skip non-SubType objects (you can use dynamic_cast<SubType*>() to check each item). There is a nice overview of implementing your own container and iterator in this O'Reilly Net article.

Rick Copeland
You must be careful not to increment the iterator beyond the end of the container. For that you will need your wrapper to keep a copy of the 'end' iterator so that you will have a stop condition. That or just use boost::filter_iterator as Sanjaya suggests in another answer.
David Rodríguez - dribeas
+9  A: 

You must use a dynamic cast.

the_iterator = the_vector.begin();
while( the_iterator != the_vector.end() ) {
    SubType* item = dynamic_cast<SubType*>(*the_iterator);
    if( item != 0 )
       ... 

    //only SubType(2) and SubType(4) should be in this loop.
    ++the_iterator;
}
Jem
This is a solution to the 'check the proper subtype' part of the question, but it has a flaw in that after reaching the last SubType element it will keep incrementing the iterator beyond the end of the container.
David Rodríguez - dribeas
dribeas, i don't see how that could ever happen. he's got the right condition at the loop's head.
Johannes Schaub - litb
Original question did not even mention dynamic cast, so I supposed that the poster did not know dynamic casts, and was looking in the wrong direction for the solution (change something related to the iterator).Before suggesting a solution (build your own iterator) that is probably more-than-necessary complex, I prefered to first propose the simpliest way to solve the original problem.Then, as my elipsis and indentation imply, the iterator must not be in the if( item != 0 ) block, of course.
Jem
Most reasonable solution.
Martin York
+12  A: 

boost filter iterator?

Sanjaya R
good answer, wanted to go for it too :) Would be even better with a small example. I would propose BOOST_FOREACH(Type *t, make_pair(make_filter_iterator(ll_dynamic_cast<SubType*>(_1), v.begin(), v.end()), make_filter_iterator(ll_dynamic_cast<SubType*>(_1), v.end(), v.end())) { ... } (using boost.iterators, boost.lambda and boost.foreach)
Johannes Schaub - litb
Ah, should've known there was already boost for this!
Andy
Great! I felt like there is should be ready to use solution. Thanks. Never know what else has boost.
Mykola Golubyev
+3  A: 

Solution without boost. But if you have an access to the boost library - use Filter Iterator as was proposed.

template <typename TCollection, typename T>
class Iterator
{
public:
    typedef typename TCollection::iterator iterator;
    typedef typename TCollection::value_type value_type;

    Iterator(const TCollection& collection,
             iterator it):
        collection_(collection),
        it_(it)
    {
        moveToNextAppropriatePosition(it_);
    }
    bool operator != ( const Iterator& rhs )
    {
        return rhs.it_ != it_;
    }
    Iterator& operator++()
    {
        ++it_;
        moveToNextAppropriatePosition(it_);
        return *this;
    }
    Iterator& operator++(int);
    Iterator& operator--();
    Iterator& operator--(int);
    value_type& operator*()
    {
        return *it_;
    }
    value_type* operator->()
    {
        return &it_;
    }
private:
    const TCollection& collection_;
    iterator it_;
    void moveToNextAppropriatePosition(iterator& it)
    {
        while ( dynamic_cast<T*>(*it) == NULL && it != collection_.end() ) 
            ++it;
    }
};

class A
{
public:
    A(){}
    virtual ~A(){}
    virtual void action()
    {
        std::cout << "A";
    }
};
class B: public A
{
public:
    virtual void action()
    {
        std::cout << "B";
    }
};
int main()
{
    typedef std::vector< A* > Collection;
    Collection c;
    c.push_back( new A );
    c.push_back( new B );
    c.push_back( new A );

    typedef Iterator<Collection, B> CollectionIterator;
    CollectionIterator begin(c, c.begin());
    CollectionIterator end(c, c.end());

    std::for_each( begin, end, std::mem_fun(&A::action) );
}
Mykola Golubyev
+2  A: 

Just another way how to do it using boost iterators. This time, using std::remove_copy_if:

std::remove_copy_if(v.begin(), v.end(), 
    boost::make_function_output_iterator(boost::bind(&someFunction, _1)),
    !boost::lambda::ll_dynamic_cast<SubType*>(boost::lambda::_1));

It will call a function (In this example someFunction. But it can be anything boost::bind can construct - also a member-function) for each pointer that's pointing to a SubType.

Johannes Schaub - litb