'Easy way to iterate through multiple std::vectors in C++

I have two std::vectors of same type, and I need to iterate through both of them using the same routine. Something like this:

std::vector<int> values1, values2;
int counter = 0;
for (int val : values1) counter += val;
for (int val : values2) counter += val;

Is there any simple way to write the last two lines in a single loop, to avoid code repetition? Something that would look like this:

std::vector<int> values1, values2;
int counter = 0;
for (int val : values1, values2) counter += val;
c++


Solution 1:[1]

If you don't need to access either vector after iterating over them, you could move them into a std::initializer_list and then iterate over that:

std::vector<int> values1{1, 2, 3};
std::vector<int> values2{4, 5, 6};

for (const auto& v : {std::move(values1), std::move(values2)}) {
    for (auto value : v) {
        std::cout << value << ' ';
    }
}

// prints "1 2 3 4 5 6 "

As ShadowRanger pointed out below, moving from the original vectors isn't necessary if we instead iterate over an initializer list of std::reference_wrappers. This can be done by swapping std::move with std::cref (or std::ref if you need to mutate them):

for (const auto& v : {std::cref(values1), std::cref(values2)}) {
    for (auto value : v.get()) {
        std::cout << value << ' ';
    }
}
// prints "1 2 3 4 5 6 ", as before

Solution 2:[2]

No, there isn't, sorry. At least not directly in the language.

Boost can do it:

for (int val : boost::range::join(values1, values2))
   counter += val;

Because Boost can do it, you could make something to do it as well, by making an iterator type of your own that casts a "view" over both collections.

But, particularly in the simple case you've shown, it's often not worth it. If your loop body is more complex, I recommend hiving it off into a function that takes int. This can just be a lambda declared immediately above the loop. That's what I do.

Solution 3:[3]

With range-v3 you could write:

namespace rs = ranges;
namespace rv = ranges::views;

int counter = rs::accumulate(rv::concat(values1, values2), 0);

I suspect there will be library support for this by C++23 (or C++2b). All that is needed is a range adaptor views::concat, and the range-ification of the <numeric> header.

Solution 4:[4]

How about:

std::vector<int> values_vecs[2];
// ... 
int counter = 0;
for (const auto& values : values_vecs)
    for (int val : values) counter += val;

It's not exactly the same as your original code, however it can support easily more than 2 vectors.

In case the vectors are there already as two separate vectors, you can still go with:

std::vector<int> values1, values2;
//... 
int counter = 0;
for (auto values_ptr : {&values1, &values2})
    for (int val : *values_ptr) counter += val;

Solution 5:[5]

I used something like this to iterate through 3 different vectors of different size where one vector was dependent of the two other vectors (first_vector is dependent on the other vectors).

#include <iostream>
#include <vector>
int main() {
  std::vector<int> first_vector{50, 20, 30, 5000};
  std::vector<int> second_vector{10};
  std::vector<int> third_vector{500, 10};
  for (int i = 0, j = 0, k = 0; i < first_vector.size(); ++i, ++j, ++k) {
    if (j == second_vector.size()) {
      j = 0;
    }
    if (k == third_vector.size()) {
      k = 0;
    }
    std::cout << first_vector[i] << '|' << second_vector[j] << '|'
              << third_vector[k] << '\n';
    std::cin.get();
  }
  return 0;
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Asteroids With Wings
Solution 3 cigien
Solution 4
Solution 5 Rase