Quite a few programming languages provide ways to iterate through a container while keeping count of the number of steps taken, such as enumerate()
in Python:
for i, elem in enumerate(v):
print(i, elem)
and enumerate()
under std::iter::Iterator
trait in Rust:
for (i, elem) in v.iter().enumerate() {
println!("{}, {}", i, elem);
}
This is just a quick note about how to do similar things in C++17 and later without declaring extra variables out of the for loop's scope.
The first way is to use a mutable lambda:
std::for_each(v.begin(), v.end(),
[i = 0](auto elem) mutable {
std::cout << i << ", " << elem << std::endl;
++i;
});
This could be used with all the algorithms that guarantees in-order application of the lambda, but I don't like the dangling ++i
that could get mixed up with other logic.
The second way utilizes structured binding in for loops:
for (auto [i, elem_it] = std::tuple{0, v.begin()}; elem_it != v.end();
++i, ++elem_it) {
std::cout << i << ", " << *elem_it << std::endl;
}
We have to throw in std::tuple
as otherwise compiler would try to create a std::initializer_list
, which does not allow heterogeneous contents.
The third least fancy method is to just calculate the distance every time:
for (auto elem_it = v.begin(); elem_it != v.end(); ++elem_it) {
auto i = std::distance(v.begin(), elem_it);
std::cout << i << ", " << *elem_it << std::endl;
}
Since we have to copy paste the starting point twice, I like other counter based approaches better.
In C++20, we have the ability to add an init-statement in ranged-based for loops, so we can write something like
for (auto i = 0; auto elem : v) {
std::cout << i << ", " << elem << std::endl;
i++;
}
Meh, not that impressive. The new <ranges>
library provides a more appealing way to achieve this:
for (auto [i, elem] : v | std::views::transform(
[i = 0](auto elem) mutable { return std::tuple{i++, elem}; })) {
std::cout << i << ", " << elem << std::endl;
}
I like the structured binding method and the <ranges>
based method the most. It would be even better though if we can get a std::views::enumerate
to solve this problem once and for all.