C++11 provides multiple ways to iterate over containers. For example:
for(auto c : container) fun(c)
for_each(container.begin(),container.end(),fun)
However what is the recommended way to iterate over two (or more) containers of the same size to accomplish something like:
for(unsigned i = 0; i < containerA.size(); ++i) {
containerA[i] = containerB[i];
}
Rather late to the party. But: I would iterate over indices. But not with the classical for
loop but instead with a range-based for
loop over the indices:
for(unsigned i : indices(containerA)) {
containerA[i] = containerB[i];
}
indices
is a simple wrapper function which returns a (lazily evaluated) range for the indices. Since the implementation – though simple – is a bit too long to post it here, you can find an implementation on GitHub.
This code is as efficient as using a manual, classical for
loop.
If this pattern occurs often in your data, consider using another pattern which zip
s two sequences and produces a range of tuples, corresponding to the paired elements:
for (auto& [a, b] : zip(containerA, containerB)) {
a = b;
}
The implementation of zip
is left as an exercise for the reader, but it follows easily from the implementation of indices
.
(Before C++17 you’d have to write the following instead:)
for (auto&& items : zip(containerA, containerB))
get<0>(items) = get<1>(items);
For your specific example, just use
std::copy_n(contB.begin(), contA.size(), contA.begin())
For the more general case, you can use Boost.Iterator's zip_iterator
, with a small function to make it usable in range-based for loops. For most cases, this will work:
template<class... Conts>
auto zip_range(Conts&... conts)
-> decltype(boost::make_iterator_range(
boost::make_zip_iterator(boost::make_tuple(conts.begin()...)),
boost::make_zip_iterator(boost::make_tuple(conts.end()...))))
{
return {boost::make_zip_iterator(boost::make_tuple(conts.begin()...)),
boost::make_zip_iterator(boost::make_tuple(conts.end()...))};
}
// ...
for(auto&& t : zip_range(contA, contB))
std::cout << t.get<0>() << " : " << t.get<1>() << "\n";
Live example.
However, for full-blown genericity, you probably want something more like this, which will work correctly for arrays and user-defined types that don't have member begin()
/end()
but do have begin
/end
functions in their namespace. Also, this will allow the user to specifically get const
access through the zip_c...
functions.
And if you're an advocate of nice error messages, like me, then you probably want this, which checks if any temporary containers were passed to any of the zip_...
functions, and prints a nice error message if so.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With