Question

I would like to create and hold on to an iterator_range. The range is constructed based on a predicate (for this example, I look for even numbers).

I can do this, but it seems I must make a copy elements from the underlying vector that is being iterated.

Please look for the comments marked ">>>" in the sample below.

Is there a way to create the iterator_range and NOT have to create a duplicate of entries from the original vector?

I looked, and have not seen, an answer to this particular situation.

#include <vector>
#include <iostream>

#include <boost/bind.hpp>
#include <boost/range.hpp>
#include <boost/foreach.hpp>
#include <boost/iterator/filter_iterator.hpp>
#include <boost/range/iterator_range.hpp>

using namespace std; 
using namespace boost; 

typedef boost::iterator_range<vector<int>::iterator> int_range;

template< class Range, class Pred >
boost::iterator_range< boost::filter_iterator< Pred, typename boost::range_iterator<Range>::type > > 
make_filter_range( Range& rng, Pred pred ) {
    return boost::make_iterator_range( 
     boost::make_filter_iterator(pred, boost::begin(rng), boost::end(rng)), 
     boost::make_filter_iterator(pred, boost::end(rng), boost::end(rng)) );
}

// This is the predicate evaluation function.
bool IsEvenFilter(int val) { return val % 2 == 0; }

void TestMakeIteratorRange()
{
    std::vector<int> vals;
    vals.push_back(1);
    vals.push_back(4);
    vals.push_back(7);
    vals.push_back(11);
    vals.push_back(16);
    vals.push_back(19);
    vals.push_back(28);

    //>>> The following int_range line does not compile.  Why?
    //>>> How can I return an int_range?
    //>>> int_range intRange = make_filter_range( vals, boost::bind(&IsEvenFilter, _1));

    //>>> The following WILL work, but it forces a second copy of elements from vals.
    std::vector<int> v2 = boost::copy_range< std::vector<int> >( 
        make_filter_range( vals, boost::bind(&IsEvenFilter, _1)));

    int_range intRange = int_range(v2);

    // Dump out contents
    BOOST_FOREACH(int &val, intRange)
    {
        cout << " " << val;
    }
    cout << endl;
}

void main()
{
    TestMakeIteratorRange();
}
Was it helpful?

Solution

int_range intRange = make_filter_range( vals, boost::bind(&IsEvenFilter, _1));

You have to store the type returned by make_filter_range. Which is not int_range.

This is incidentally why auto exists (in C++11); so that you don't have to type that return value if you want to store what the function returns. If you don't have access to C++11 auto, use BOOST_AUTO instead.

If you can't use that for some reason, you can also use any_range. Which, as the name suggests, can store any range for a specific type.

Also, consider using the proper Boost range-adapters, like boost::adaptors::filtered instead of make_filter_iterator.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top