Iterator for C++11 map values (simple and transparent) - c++

I am looking for a simple way to create an iterator for the values of a map in C++11.
This method should be simple and transparent: simple in that it should be easy to implement, and transparent in that the client should not know the values come from a map, not a set.
This question has been asked several times before. Many of these questions predate C++11 and use boost, which I do not want to use. Some are not simple, John Ahlgren's solution here, http://john-ahlgren.blogspot.com/2013/10/how-to-iterate-over-values-of-stdmap.html , for example requires a page of code to write a custom iterator.
The others are not transparent, i.e., clearly one can write:
map<string,foo> mymap;
for (auto it=mymap.begin();it!=mymap.end();++it){
Foo val= it->second;
...
}
However, I do not want to do this because I do not want the client to have to know of the data representation.
The problem comes up as follows.
I have a bunch of objects uniquely indexed with a long "key". Sometimes I want to manipulate sets of these objects. Other times I want to retrieve an object given its key.
I cannot use the "set" class directly for several reasons, chief among which is that it does not store mutable instances, and these instances must be mutable (except, obviously, for the key).
So, I have decided to store all my objects in a giant global hashtable:
map<long,Foo> all_the_objects;
I then do not work with set<Foo> at all. Instead I work with set<long> and use an adaptor to simulate a set of Foo, i.e.,
class SetOfFoo{
private: set<long> theKeys;
public:
void insert(const & Foo);
size_t size() return theKeys.size();
bool is_member(const & Foo)
{return theKeys.find(Foo.key)
!= theKeys.end;}
Foo & insert(const & Foo val){
long key=val.key;
all_the_objects[key]=val;
return all_the_objects[key];
}
...::iterator begin() {???}
}
In other words, the client of the SetOfFoo class does not know or need to know that SetOfFoo is implemented as as set of keys.
I also cannot just make a Vector myself in the adaptor class, because one cannot store references in C++ collections.
Is it really impossible to make a simple, transparent way to iterate over map<> values? I find it hard to believe, as this is a very common need and is trivial to do in every language I have seen that has hashtables. I just don't understand how this can be hard.

it's pretty trivial.
Here's an extremely simplistic version that minimally solves the problem for a map of ints to strings. You can either rewrite for the types you want or templatise it as you wish.
#include <map>
#include <iostream>
#include <iterator>
#include <string>
#include <algorithm>
struct map_value_iterator : public std::map<int, std::string>::const_iterator
{
map_value_iterator(std::map<int, std::string>::const_iterator src)
: std::map<int, std::string>::const_iterator(std::move(src))
{
}
// override the indirection operator
const std::string& operator*() const {
return std::map<int, std::string>::const_iterator::operator*().second;
}
};
using namespace std;
int main()
{
map<int, string> myMap { {1, "Hello" }, { 2, "World" } };
copy(map_value_iterator(begin(myMap)), map_value_iterator(end(myMap)), ostream_iterator<string>(cout , " "));
cout << endl;
return 0;
}
Program output:
Compiling the source code....
$g++ -std=c++11 main.cpp -o demo -lm -pthread -lgmpxx -lgmp -lreadline 2>&1
Executing the program....
$demo
Hello World

You can do something like the following (C++98):
#include <iostream>
#include <map>
#include <string>
#include <algorithm>
#include "util/pair_iterator.hpp"
template<class T> inline T const& constify(T& t) { return t; }
int main()
{
using namespace std;
using namespace util;
map<int, string> m;
m[0] = "alice";
m[1] = "bob";
m[2] = "carol";
m[3] = "dave";
m[4] = "eve";
copy(
over_second(m.begin())
, over_second(m.end())
, ostream_iterator<string>(cout, "\n")
);
copy(
over_first(m.begin())
, over_first(m.end())
, ostream_iterator<int>(cout, "\n")
);
// const iterators check
copy(
over_second(constify(m).begin())
, over_second(constify(m).end())
, ostream_iterator<string>(cout, "\n")
);
copy(
over_first(constify(m).begin())
, over_first(constify(m).end())
, ostream_iterator<int>(cout, "\n")
);
}
Here is an implementation:
// util/pair_iterator.hpp
#include <iterator>
#include "boost/iterator/transform_iterator.hpp"
#include "boost/type_traits/remove_reference.hpp"
#include "boost/type_traits/is_const.hpp"
#include "boost/mpl/if.hpp"
namespace util {
namespace aux {
template<class T> struct dereference_type
: boost::remove_reference<typename std::iterator_traits<T>::reference>
{
};
template<class PairT>
struct first_extracter
{
typedef typename boost::mpl::if_<
boost::is_const<PairT>
, typename PairT::first_type const
, typename PairT::first_type
>::type result_type;
result_type& operator()(PairT& p) const { return p.first; }
};
template<class PairT>
struct second_extracter
{
typedef typename boost::mpl::if_<
boost::is_const<PairT>
, typename PairT::second_type const
, typename PairT::second_type
>::type result_type;
result_type& operator()(PairT& p) const { return p.second; }
};
} // namespace aux {
template<class IteratorT>
inline
boost::transform_iterator<aux::first_extracter<typename aux::dereference_type<IteratorT>::type>, IteratorT>
over_first(IteratorT const& i)
{
typedef aux::first_extracter<typename aux::dereference_type<IteratorT>::type> extracter;
return boost::transform_iterator<extracter, IteratorT>(i, extracter());
}
template<class IteratorT>
inline
boost::transform_iterator<aux::second_extracter<typename aux::dereference_type<IteratorT>::type>, IteratorT>
over_second(IteratorT const& i)
{
typedef aux::second_extracter<typename aux::dereference_type<IteratorT>::type> extracter;
return boost::transform_iterator<extracter, IteratorT>(i, extracter());
}
} // namespace util

Related

Can I use lambda as a hashing function in boost::multi_index hash-like interface?

Is it possible to use lambda for hashing in hashed_<non>_unique interface for boost::multi_index?
See this example: https://godbolt.org/z/1voof3
I also saw this: How to use lambda function as hash function in unordered_map? where the answer says:
You need to pass lambda object to unordered_map constructor since lambda types are not default constructible.
and I'm not sure is it even possible to do for the given example on godbolt.
Starting with C++20, yes, you can: https://godbolt.org/z/fTbzPP (note f is declared as auto const hash_f, without a &).
As for #sehe's claim that multi_index_containers can't be passed instances of hash objects (or other intervening function objects) at construction time, the claim is incorrect: they can, although the interface is somewhat complicated:
Live Coliru Demo
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/identity.hpp>
#include <functional>
struct non_default_ctble_hash
{
non_default_ctble_hash(std::size_t n):n{n}{}
template<typename T>
std::size_t operator()(const T& x){return std::hash<T>{}(x)*n;}
std::size_t n;
};
using namespace boost::multi_index;
using container=multi_index_container<
int,
indexed_by<
hashed_unique<identity<int>,non_default_ctble_hash>
>
>;
int main()
{
container::ctor_args_list cal{
{0,identity<int>{},non_default_ctble_hash{666},std::equal_to<int>{}}
};
container c(cal);
}
I don't think you can. With a standard container you would have had to supply the actual instance to the constructor. However, MultiIndex doesn't afford that:
docs
As explained in the index concepts section, indices do not have public constructors or destructors. Assignment, on the other hand, is provided. Upon construction, max_load_factor() is 1.0.
Loophole?
You can perhaps get away with a locally defined class:
auto const hash_f = [](int const& n) { return std::hash<int>()(n); };
struct HashType : decltype(hash_f) {};
using AnimalsMultiIndex = multi_index_container<
Animal, indexed_by<hashed_non_unique<
tag<animal_legs>, member<Animal, LegsType, &Animal::legs>,
HashType>>>;
AnimalsMultiIndex animals;
Which does work: c++20 required
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/tag.hpp>
#include <boost/multi_index_container.hpp>
#include <iostream>
#include <string>
using namespace boost::multi_index;
using LegsType = int;
struct Animal {
std::string name;
LegsType legs;
};
// tags
struct animal_legs {};
int main() {
// using lambda doesn't work for hashing
auto const hash_f = [](int const& n) { return std::hash<int>()(n); };
struct HashType : decltype(hash_f) {};
using AnimalsMultiIndex = multi_index_container<
Animal, indexed_by<hashed_non_unique<
tag<animal_legs>, member<Animal, LegsType, &Animal::legs>,
HashType>>>;
AnimalsMultiIndex animals;
animals.insert({ "cat", 4 });
auto const& legs_index = animals.get<animal_legs>();
int num_of_legs = 4;
std::cout << "Number of animals that have " << num_of_legs
<< " legs is: " << legs_index.count(num_of_legs) << '\n';
}
Prints
Number of animals that have 4 legs is: 1

std::pair of strings as custom key of unordered_map defined in std fails with template errors

I have a map defined and used like this
// def.h
struct X{};
struct Y{};
struct myStruct
{
X x;
Y y;
};
typedef std::unordered_map<std::pair<std::string, std::string>, myStruct> myMap;
namespace std
{
template<> struct pair<std::string, std::string>
{
std::string s1,s2;
pair(const std::string& a, const std::string& b):s1(a),s2(b){}
bool operator < (const pair<std::string,std::string>& r)
{
return (0 < r.s1.compare(s1) && (0 < r.s2.compare(s2)));
}
};
}
//use.cpp
class CUse
{
myMap m;
public:
CUse():m(0){}
};
Some errors emitted by the compiler are extracted as below
At the constructor CUse initialization,
note: see reference to function template instantiation
'std::unordered_map,myStruct,std::hash<_Kty>,std::equal_to<_Kty>,std::allocator>>::unordered_map(unsigned __int64)' being compiled
At the declaration of m in CUse
note: see reference to class template instantiation
'std::unordered_map,myStruct,std::hash<_Kty>,std::equal_to<_Kty>,std::allocator>>' being compiled
As #Bo Persson and #Sean Cline mentioned in the comments, you will need to use a custom hash function/functor to do that.
LIVE DEMO
#include <unordered_map>
#include <string>
#include <tuple>
#include <functional>
#include <cstddef>
#include <iostream>
struct myStruct { int x, y; };
using Key = std::pair<std::string, std::string>;
namespace something
{
struct Compare //custom hash function/functor
{
std::size_t operator()(const Key& string_pair) const
{
// just to demonstrate the comparison.
return std::hash<std::string>{}(string_pair.first) ^
std::hash<std::string>{}(string_pair.second);
}
};
}
using myMap = std::unordered_map<Key, myStruct, something::Compare>;
int main()
{
myMap mp =
{
{ { "name1", "name2" },{ 3,4 } },
{ { "aame1", "name2" },{ 8,4 } },
{ std::make_pair("fame1", "name2"),{ 2,4 } }, // or make pair
{ std::make_pair("fame1", "bame2"),{ 1,2 } }
};
for(const auto& it: mp)
{
std::cout << it.first.first << " " << it.first.second << " "
<< it.second.x << " " << it.second.y << std::endl;
}
return 0;
}
However, every symmetric pair will make almost same hashes and that can cause,
hash collisions and thereby less performance. Nevertheless, additional specializations for std::pair to compose hashes are available in boost.hash
An alternative solution, could be using std::map<>. There you can also specify the custom function/functor for the std::pair, in order to achieve the same map structure. Even though there you will not have to face hash-collisions, that would be well sorted which you might not want.
LIVE DEMO
#include <map>
#include <string>
#include <tuple>
#include <iostream>
struct myStruct { int x, y; };
using Key = std::pair<std::string, std::string>;
namespace something
{
struct Compare
{
bool operator()(const Key& lhs, const Key& rhs) const
{
// do the required comparison here
return std::tie(lhs.first, lhs.second) < std::tie(rhs.first, rhs.second);
}
};
}
using myMap = std::map<Key, myStruct, something::Compare>;
could you tell me why it is not good to use my data type in std ? My
type is defined in my own program anyway.
You shouldn't make it under the namespace of std, because it can cause a UB. A well defined situations/exceptions where you can extend std namespace are given here: https://en.cppreference.com/w/cpp/language/extending_std
Answer to the secondary question:
Thank you but could you tell me why it is not good to use my data type in std ? My type is defined in my own program anyway
You feel that you can define whatever you want in your program, right? (That is the impression you gave, at least.) Well, C++ implementations feel the same way about namespace std -- it is their namespace and they can define whatever they want in it (subject to the C++ standard, of course).
If an implementation needs to define a (possibly undocumented) helper function/class/whatever, the expectation is that it can be placed in namespace std without conflicting with your program. Case in point: what would happen to your program if your C++ library decided that it needed to define a specialization of the std::pair template for std::pair<std::string, std::string>? To my knowledge, the standard neither requires nor prohibits such a specialization, so the existence of it is left to the implementor's discretion.
Namespaces exist to prevent naming conflicts. In particular, namespace std exists to isolate C++ implementation details from user programs. Adding your code to namespace std destroys that isolation, hence the standard declares it undefined behavior. Don't do it.
(That being said, there is nothing stopping you from writing a wrapper class around std::pair<std::string, std::string> to get the functionality you need. Just do it in your own namespace.)
You need to define a specialization of std::hash for your key type, like so:
#include <unordered_map>
#include <string>
using KeyType = std::pair<std::string, std::string>;
namespace std
{
template<>
struct hash<KeyType>
{
size_t operator()(KeyType const& kt) const
{
size_t hash = 0;
hash_combine(hash, kt.first);
hash_combine(hash, kt.second);
return hash;
}
// taken from boost::hash_combine:
// https://www.boost.org/doc/libs/1_55_0/doc/html/hash/reference.html#boost.hash_combine
template <class T>
inline static void hash_combine(std::size_t& seed, const T& v)
{
std::hash<T> hasher;
seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2);
}
};
}
int main()
{
std::unordered_map<KeyType, int> us;
return 0;
}

C++ Mutually Recursive Variant Type (Again)

I have a problem similar to that described here: C++ Mutually Recursive Variant Type
I am trying to create a JSON representation in C++. Many libraries already offer excellent JSON representations and parsers that are very fast, but I am not reinventing this wheel. I need to create a C++ JSON representation that supports certain space optimizations under specific conditions. In short, if and only if a JSON array contains homogenous data, rather than storing every element as bloated variant types, I need compact storage of native types. I also need to support heterogeneous arrays and standard nested JSON objects.
The following is the "if wishes were horses, beggars would ride" version of the code, which is meant to clearly illustrate intent, but is obviously broken because types are used before any declaration exists. I want to avoid specifying the same information multiple times in types (i.e. Array, Object, and Value should not require duplicated type specifications). I also want to avoid any unnecessarily high run-time costs.
#include <string>
#include <unordered_map>
#include <vector>
#include <boost/variant.hpp>
#include <boost/variant/variant.hpp>
#include <boost/variant/recursive_wrapper.hpp>
class JSONDocument {
public:
using String = std::string;
using Integer = long;
using Float = double;
using Boolean = bool;
using Null = void *;
using Key = std::string;
using Path = std::string;
using Value = boost::variant<
Null,
String,
Integer,
Float,
Boolean,
Object,
Array
>;
using Object = std::unordered_map<Key,Value>;
using Array = boost::variant<
std::vector<Null>,
std::vector<String>,
std::vector<Integer>,
std::vector<Float>,
std::vector<Boolean>,
std::vector<Value> >;
private:
Value root;
class value_traversal_visitor : public boost::static_visitor<Value> {
public:
value_traversal_visitor( Path path ) : path(path) {}
Value operator()( Null x ) const {
if( path.empty() ) {
return x;
}
// otherwise throw ...
}
Value operator()( String x ) const {
if( path.empty() ) {
return x;
}
}
...
// special handling for Array and Object types
private:
Path path;
};
public:
Value get( Path path ) {
return boost::apply_visitor( value_traversal_visitor( path ), root );
}
...
};
As you can see, I am including the recursive_wrapper header. I have tried various invocations of boost::make_recursive_variant and boost::recursive_wrapper, but I always get compiler errors. I do not see how the answer from C++ Mutually Recursive Variant Type solves this, because in every attempt, I get compiler errors (from both gcc++ 5.3 and LLVM/clang++ 3.8) that almost exclusively reference Boost that essentially boil down to types not being convertible or declarations either conflicting or not existing. I would put one of my attempts along with specific compiler error messages here, but I wouldn't know which of the many attempts to use.
I'm hoping somebody can set me on the right path...
Thanks in advance!
Edit
Just to build on the accepted answer below, here is an example of a working skeleton for the types and their usages.
#include <string>
#include <unordered_map>
#include <vector>
#include <boost/variant.hpp>
#include <boost/variant/variant.hpp>
#include <boost/variant/recursive_wrapper.hpp>
using String = std::string;
using Integer = long;
using Float = double;
using Boolean = bool;
using Key = std::string;
using Value = boost::make_recursive_variant<
String,
Integer,
Float,
Boolean,
std::unordered_map<Key, boost::recursive_variant_>,
boost::variant<std::vector<String>,std::vector<Integer>,std::vector<Float>,std::vector<Boolean>,std::vector<boost::recursive_variant_> >
>::type;
using Object = std::unordered_map<Key, Value>;
using Array = boost::variant<std::vector<String>,std::vector<Integer>,std::vector<Float>,std::vector<Boolean>,std::vector<Value> >;
int main( int argc, char* argv[] ) {
Value v;
v = static_cast<Integer>( 7 );
Object o;
v = o;
Array a = std::vector<Integer>( 3 );
v = a;
return 0;
}
You could just use recursive_variant_ placeholder with make_recursive_variant.
Here's the gist:
using Value = boost::make_recursive_variant<
Null,
String,
Integer,
Float,
Boolean,
std::unordered_map<Key, boost::recursive_variant_>, // Object
std::vector<boost::recursive_variant_> // Array
>::type;
using Object = std::unordered_map<Key, Value>;
using Array = boost::variant<Value>;
Live Demo
Live On Coliru
As you can see there's unimplemented bits in the code (never write functions missing return statements!). Also note the simplifications in control flow for get and the private visitor implementation.
#include <boost/variant.hpp>
#include <boost/variant/recursive_wrapper.hpp>
#include <boost/variant/variant.hpp>
#include <string>
#include <unordered_map>
#include <vector>
class JSONDocument {
public:
struct Null { constexpr bool operator==(Null) const { return true; } };
using String = std::string;
using Integer = long;
using Float = double;
using Boolean = bool;
using Key = std::string;
using Path = std::string;
using Value = boost::make_recursive_variant<
Null,
String,
Integer,
Float,
Boolean,
std::unordered_map<Key, boost::recursive_variant_>, // Object
std::vector<boost::recursive_variant_> // Array
>::type;
using Object = std::unordered_map<Key, Value>;
using Array = boost::variant<Value>;
private:
Value root;
struct value_traversal_visitor {
Path path;
using result_type = Value;
result_type operator()(Value const &x) const {
if (path.empty()) {
return x;
}
return boost::apply_visitor(*this, x);
}
result_type operator()(Null) const { throw std::invalid_argument("null not addressable"); }
result_type operator()(String const &) const { throw std::invalid_argument("string not addressable"); }
// special handling for Array and Object types TODO
template <typename T> result_type operator()(T &&) const { return Null{}; }
};
public:
Value get(Path path) { return value_traversal_visitor{path}(root); }
};
int main() {}
CAVEATS
Note that you should NOT use void* for Null because all manner of unwanted implicit conversions
Note that you should probably not use unordered_map because
some JSON implementations allow duplicate property names
some JSON applications depend on the ordering of the properties
See also https://github.com/sehe/spirit-v2-json/blob/master/json.hpp#L37
Not a solution per se, but Here's a way to achieve variant recursivity using std::variant. I thought this might be of interest, since the stl doesn't provide any api for recursive, nor forward-declared types. Compiles using gcc 7.2 -std=c++17
#include <variant>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
struct Nil {};
struct vector1;
using var_t1 = variant<Nil, int, vector1>;
using var_t2 = variant<Nil, double, float, int, var_t1>;
struct vector1 {
vector<var_t2> v_;
};
struct print_var_t2;
struct print_var_t1 {
void operator()(const vector1& v);
void operator()(int) { cout << "int\n"; }
void operator()(const Nil&) { cout << "nil\n"; }
};
struct print_var_t2 {
void operator()(const Nil&) { cout << "Nil\n"; }
void operator()(int) { cout << "int\n"; }
void operator()(double) { cout << "double\n"; }
void operator()(float) { cout << "float\n"; }
void operator()(const var_t1& v);
};
void print_var_t1::operator()(const vector1& v) {
for_each(v.v_.begin(), v.v_.end(), [](const var_t2& x)
{
visit(print_var_t2{}, x);
});
}
void print_var_t2::operator()(const var_t1& v) {
visit(print_var_t1{}, v);
}
int main()
{
vector1 v1;
v1.v_.push_back(.1);
v1.v_.push_back(2.f);
v1.v_.push_back(3);
v1.v_.push_back(var_t2{3});
var_t1 var1 = v1;
std::visit(print_var_t1{}, var1);
return 0;
}

Spirit Boost real_p parser behaving strangely

I have a following problem: I am reusing old code that uses real_p parser to parse real numbers. I would like to capture each number, convert it to string, and put it in vector of strings.
I am using the following code, where l_double is a variable of the type double, convertdouble function converts a double to a string, and result.m_literalValues is the vector of strings.
However, the code will not assign a parsed value to l_double.
rule<> alternative3 = +(real_p [assign_a(l_double)]
[push_back_a(result.m_LiteralValues, convertdouble(l_double))]
>> str_p(",")
)
Does anyone has an idea what am I doing wrong ?
Note: I will not re-engineer the old code, which is much more complex than the example given. I just want to extract the strings of all values parsed, and to put them in the vector of strings.
The problem seems to be located in push_back_a(result.m_LiteralValues, convertdouble(l_double)), specifically in convertdouble(l_double). push_back_a requires that its second argument be a reference to be stored in a "policy holder actor" so using the function call there causes the error. If you don't need to store l_double and were simply using it as a temporary, one way to accomplish what you want is creating your own phoenix function that behaves similarly to push_back_a as explained here(full example here).
You define the phoenix function like this:
struct push_back_impl
{
template <typename Container, typename Item>
struct result
{
typedef void type;
};
template <typename Container, typename Item>
void operator()(Container& c, Item const& item) const
{
c.push_back(convertdouble(item));
}
};
function<push_back_impl> const push_back = push_back_impl();
and then define your rule like this:
rule<> alternative3 = +( real_p[push_back(var(result.m_LiteralValues),arg1)] >> str_p(",") );
Full compilable code(change the for loop to show the results if you can't/don't want to use c++11):
#include <boost/spirit/include/classic_core.hpp>
#include <boost/spirit/include/classic_operators.hpp>
#include <boost/spirit/include/phoenix1_functions.hpp>
#include <boost/spirit/include/phoenix1_primitives.hpp>
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
using namespace boost::spirit::classic;
using namespace phoenix;
std::string convertdouble(const double& d)
{
std::stringstream ss;
ss<<d;
return ss.str();
}
struct push_back_impl
{
template <typename Container, typename Item>
struct result
{
typedef void type;
};
template <typename Container, typename Item>
void operator()(Container& c, Item const& item) const
{
c.push_back(convertdouble(item));
}
};
function<push_back_impl> const push_back = push_back_impl();
struct Results
{
std::vector<std::string> m_LiteralValues;
};
int main()
{
Results result;
char const* test="2.5,3.6,4.8,";
rule<> alternative3 = +( real_p[push_back(var(result.m_LiteralValues),arg1)] >> str_p(",") );
if(parse(test,alternative3,space_p).full)
{
std::cout << "success" << std::endl;
for(auto& str :result.m_LiteralValues)
std::cout << str << std::endl;
}
else
{
std::cout << "failure" << std::endl;
}
return 0;
}

How to return an iterator_range

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();
}
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.