Range based for or Boost foreach: can it be used with two containers?

Q1.
Consider a scenario where we have two vector<int> conatainers of equal length, say f and s, and we have to do pairwise processing, ie work with the pairs f[i], s[i].

Anyway to do this with range based for?


Q2.
Anyway to use range based for, to process a sunrange of a container?
Eg, often we have to treat the first or the last element of a container as special. Can we use range based for or foreach for the rest of the container?
I'm not well familiar with either, but I'll try to help.
You can do 2 with boost using a pair: http://ideone.com/bnIf6
With a little wrapping, I'm sure both 1 and 2 can be done with either method though.
I'll post code a bit later..

Edit: 2 with a wrapper. I think this should work with for too, but ideone doesn't seem to support that yet. http://ideone.com/fgjQI

Edit: I don't think I can do 1. My Idea is to write a class zip<container1, container2> and an iterator for it such that using it would look like an iterator of container of pair<container1::value_type, container2::value_type>. The problem is that iterator needs operators * and -> overloaded, which work with pointers and references, and pointers to objects that don't exist can't be a good idea. Maybe it would be best to zip them manually.
Last edited on
Thanks hamsterman, also thanks for http://ideone.com, I didnt know about that side.
I would never have guessed the behavior of foreach with an iterator pair!!

Problem 1: For efficiency purposes, I wouldnt want to explicitly zip two containers. No big deal, I was just wondering if there was a simple way to do this (like your solution to the second problem)!!

Edit: 2 with a wrapper. I think this should work with for too, but ideone doesn't seem to support that yet. http://ideone.com/fgjQI

ideone doesnt support what yet?
Any advantages of this second wrapper approach as compared to the simpler first one?
I would never have guessed the behavior of foreach with an iterator pair!!
That's what documentation is for.

ideone doesnt support what yet?
a range based for.

Any advantages of this second wrapper approach as compared to the simpler first one?
I don't think a range based for would work with an iterator pair. That's the only reason.

Here, I did an ugly version of 1: http://ideone.com/ylho8
Problems:
* If you zip container A with container B, you iterate over pairs of their iterators, rather than pairs of their values. Notice how unsafe it is, should you ever want to remember the address of the value pointed by the iterator.
* If A and B have different lengths, this will fail, because then iterator will never match end(). This could be fixed by changing && in == to ||, but then == wouldn't be an equivalence relation any more.
* I've never written an iterator before. Most of the functionality is bullshit. If you really need code like this, I suggest you do some research and write more solid container and iterator classes.
Thanks hamsterman for all the effort!

You are right, problem 1 is kind of un-pretty. I'll stick to the usual iterator approach (non-range based) for problem 1
Last edited on
boost actually has a zip iterator, no need to reinvent: http://www.boost.org/doc/libs/release/libs/iterator/doc/zip_iterator.html

as for subranges, consider the boost.range library (which I love ever so much for map_values and the indirected adaptors). Subrange: http://www.boost.org/doc/libs/release/libs/range/doc/html/range/reference/utilities/sub_range.html
Thanks Cubbi!
I dont quite understand sub_range and iterator_range; will hunt for some more examples for those!
Here is some fun I just spent time with.

The idea with a ranged for is that it uses the Boost Range stuff. Hence, you need to provide both an iterator-type access and a container-type access (specifically those begin() and end() functions down there).

Keep in mind that what follows is just a short hack; it is missing stuff that would make it generally usable outside of the example.

(What it does is essentially what the Boost zip_iterator does.)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
// This is C++11 code!
#include <iterator>
#include <utility>

template <typename Iterator1, typename Iterator2>
struct zip_iterator_t
  {
  Iterator1 first, end1;
  Iterator2 second, end2;

  zip_iterator_t() { }

  zip_iterator_t(
    Iterator1 begin1, Iterator1 end1,
    Iterator2 begin2, Iterator2 end2
    ):
    first ( begin1 ), end1( end1 ),
    second( begin2 ), end2( end2 )
    { }

  zip_iterator_t& operator ++ ()
    {
    if (first  != end1) ++first;
    if (second != end2) ++second; 
    return *this;
    }
  zip_iterator_t operator ++ (int) const
    {
    zip_iterator_t result( *this );
    operator ++ ();
    return result;
    }

  bool ok() const { return (first != end1) && (second != end2); }

  bool operator == ( const zip_iterator_t& that ) const
    {
    return ok() == that.ok();
    }
  bool operator != ( const zip_iterator_t& that ) const
    {
    return ok() != that.ok();
    }

  typedef std::pair <
    typename std::iterator_traits <Iterator1> ::value_type,
    typename std::iterator_traits <Iterator2> ::value_type> value_type;

  value_type operator * () const
    {
    return std::make_pair( *first, *second );
    }

  zip_iterator_t begin() const { return *this; }
  zip_iterator_t end()   const { return zip_iterator_t(); }
  };

template <typename Iterator1, typename Iterator2> inline
struct zip_iterator_t <Iterator1, Iterator2>
zip_iterator(
  Iterator1 begin1, Iterator1 end1,
  Iterator2 begin2, Iterator2 end2
  ) {
  return zip_iterator_t <Iterator1, Iterator2> ( begin1, end1, begin2, end2 );
  }

template <template <typename...> class Container, typename T>
Container <std::pair <T, T>>
zip( const Container <T> & a, const Container <T> & b )
  {
  Container <std::pair <T, T>> result;
  for (auto zi : zip_iterator( a.begin(), a.end(), b.begin(), b.end() ))
    {
    result.push_back( zi );
    }
  return result;
  }

#include <iostream>
#include <vector>
using namespace std;

int main()
  {

  vector <string> boys(  { "Aaron",  "Basil",    "Cedric", "Derrik", "Edward", "Ferris", "Gordon" } );
  vector <string> girls( { "Alanna", "Brittany", "Cara",   "D'Lynn", "Erin",   "Farrah" } );

  typedef pair <string, string> couple_t;
  vector <couple_t> couples = zip( boys, girls );

  for (auto couple: couples)
    {
    cout << couple.first << " and " << couple.second
         << ", sitting in a tree, K-I-S-S-I-N-G.\n";
    }

  cout << "Poor Gordon!\n";

  return 0;
  }

Enjoy!
Topic archived. No new replies allowed.