Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Iterating over more than one seq. container in C++11

I need a piece of advice for the following situation - I can't figure it out for hours: How to walk through more than one seq . containers of same size (here: two vectors) in a simple way?

int main() {
  int size = 3;
  std::vector<int> v1{ 1, 2, 3 }, v2{ 6, 4, 2 };

  // old-fashioned - ok
  for (int i = 0; i < size; i++) {
    std::cout << v1[i] << " " << v2[i] << std::endl;
  }

  // would like to do the same as above with auto range-for loop
  // something like this - which would be fine for ONE vector.
  // But this does not work. Do I need a hand-made iterator instead?
  for (const auto& i:v1,v2) {
    std::cout << i << " " << i << std::endl;
  }

  return EXIT_SUCCESS;
}

Thank you!

like image 329
Excalibur Avatar asked Mar 03 '14 08:03

Excalibur


2 Answers

There is boost::combine() in Boost.Range that allows one to write

#include <iostream>
#include <iterator>
#include <vector>
#include <boost/range/combine.hpp>

int main()
{
    std::vector<int> v1{ 1, 2, 3 }, v2{ 6, 4, 2 };

    for (auto&& t : boost::combine(v1, v2))
            std::cout << t.get<0>() << " " << t.get<1>() << "\n";    
}

Live Example.

If you don't like to rely on this, you can spell out the combine() functionality yourself with Boost.Iterator's zip_iterator and Boost.Range's iterator_range and a little bit of C++14 deduced return-types:

template<class... Ranges>
auto combine(Ranges const&... ranges) 
// add -> decltype( boost::make_iterator_range(...) ) in C++11
{
    return boost::make_iterator_range(
        boost::make_zip_iterator(boost::make_tuple(begin(ranges)...)),
        boost::make_zip_iterator(boost::make_tuple(end(ranges)...))        
    );
}

Live Example.

Explanation: boost::make_zip_iterator creates a boost::tuple of iterators into your input ranges, and overloads all the usual operator++ and operator* that you know and love from regular iterators. The iterator_range then wraps two of these zip_iterators into a package with a begin() and end() function that allows it to be used by the C++11 range-for loop. It generalizes to more than two input ranges as well. You can unpack the K-th element from a tuple with the .get<K> member function.

like image 91
TemplateRex Avatar answered Nov 16 '22 02:11

TemplateRex


The range-based for loop was designed as a convenience for iterating one range, because it's by far the most common case. If you need to iterate multiple ranges, which is not that most common case, you can still do it the traditional way:

for (auto i1 = begin(v1), i2 = begin(v2), e = end(v1); i1 != e; ++i1, ++i2)
{
  // processing
}
like image 36
Angew is no longer proud of SO Avatar answered Nov 16 '22 00:11

Angew is no longer proud of SO