How to Iterate Over Two Vectors Simultaneously Using Boost_Foreach

How can I iterate over two vectors simultaneously using BOOST_FOREACH?

Iterating over two things simultaneously is called a "zip" (from functional programming), and Boost has a zip iterator:

The zip iterator provides the ability to parallel-iterate over several
controlled sequences simultaneously. A zip iterator is constructed
from a tuple of iterators. Moving the zip iterator moves all the
iterators in parallel. Dereferencing the zip iterator returns a tuple
that contains the results of dereferencing the individual iterators.

Note that it's an iterator, not a range, so to use BOOST_FOREACH you're going to have to stuff two of them into an iterator_range or pair. So it won't be pretty, but with a bit of care you can probably come up with a simple zip_range and write:

BOOST_FOREACH(boost::tuple<int,int> &p, zip_range(v1, v2)) {
doSomething(p.get<0>(), p.get<1>());
}

Or special-case for 2 and use std::pair rather than boost::tuple.

I suppose that since doSomething might have parameters (int&, int&), actually we want a tuple<int&,int&>. Hope it works.

BOOST_FOREACH and a vector

BOOST_FOREACH behaves exactly as you tell him, by value, reference or const reference

C++11 for each loop with more than one variable

There is no built-in way to do this. If you can use Boost, boost::combine will work for iterating two (or more) ranges simultaneously (Does boost offer make_zip_range?, How can I iterate over two vectors simultaneously using BOOST_FOREACH?):

for (boost::tuple<int&, int&> ij : boost::combine(a, b)) {
int& i = boost::get<0>(ij);
int& j = boost::get<1>(ij);
// ...
}

Unfortunately accessing the elements within the tuple elements of the zipped range is highly verbose. C++17 will make this much more readable using structured binding:

for (auto [&i, &j] : boost::combine(a, b)) {
// ...
}

Since you don't need to break out of the loop or return from the enclosing function, you could use boost::range::for_each with the body of your loop as a lambda:

boost::range::for_each(a, b, [](int& i, int& j)
{
// ...
});

Redefine BOOST_FOREACH macro safely

You could use an inline helper function.

#define FOREACH(decl, c) BOOST_FOREACH(decl, pair_helper(c))

template <typename T>
inline std::pair<typename T::iterator, typename T::iterator> pair_helper (T c) {
return std::make_pair(c.begin(), c.end());
}

BOOST_FOREACH & templates without typedef

There is a problem because it is a macro, and therefore cannot handle types containing commas (preprocessor doesn't know about templates).

You can also declare the variable before the loop, see documentation.

std::map<int, double> my_map;

//1)
typedef std::pair<int, double> MyPair;
BOOST_FOREACH(MyPair p, my_map) { ... }

//2)
std::pair<int, double> p;
BOOST_FOREACH(p, my_map) { ... }

Edit:

There is a further complication with std::map in particular: the value_type is not std::pair<Key, Value>, but std::pair<const Key, Value>.

Hence, if you go with the typedef, a more proper way (and the only way if you want to use a reference in the foreach loop) is to use

typedef std::pair<const int, double> MyPair;
//or
typedef std::map<int, double>::value_type MyPair;

BOOST_FOREACH(MyPair& ref, my_map) { ... }

However, that won't work if you want to use a variable declared before the loop, since you can't assign to a std::pair<const int, double> instance later (can't assign to the const field), in which case you can only use pair<int, double> as boost's manual shows.



Related Topics



Leave a reply



Submit