11

In Python there is enumerate which takes a sequence/iterator and yields pairs of an integer index and the value itself. In C++ I occasionally find myself writing

for (size_t i = 0; i != vector.size(); ++i) {
    auto const &elem = vector[i];
    // ...

Similar to Python I would like to write

for (auto const &it : enumerate(vector)) {
    // it.first is the index (size_t)
    // it.second is the element (T const&)

Does such an enumerate exist in either the STL or a common library like Boost?

Martin Ueding
  • 8,245
  • 6
  • 46
  • 92

4 Answers4

17

Yes, this is what Boost's adapators::indexed does.

Their example (which also uses the now-redundant Boost.Assign for terse container initialisation) follows:

#include <boost/range/adaptor/indexed.hpp>
#include <boost/assign.hpp>
#include <iterator>
#include <iostream>
#include <vector>


int main(int argc, const char* argv[])
{
    using namespace boost::assign;
    using namespace boost::adaptors;

    std::vector<int> input;
    input += 10,20,30,40,50,60,70,80,90;

    for (const auto& element : input | indexed(0))
    {
        std::cout << "Element = " << element.value()
                  << " Index = " << element.index()
                  << std::endl;
    }

    return 0;
}

Nothing in the standard library, though it's not hard to write.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
3

Here is an example using range-v3. A bit more verbose than a handcrafted solution, but it's nice IMHO how you can assemble such a range from existing views.

#include <range/v3/view/indices.hpp>
#include <range/v3/view/zip.hpp>

using namespace ranges;

std::vector<int> vec{42, 43, 44};

for (const auto& idxAndValue : view::zip(view::indices, vec))
   std::cout << ideAndValue.first << " : " << idxAndValue.second << "\n";;
lubgr
  • 37,368
  • 3
  • 66
  • 117
2

Another way that only works:

  1. with references to elements, and
  2. array-based containers, and
  3. elements do not overload operator&


for(auto const &it : vector) {
    size_t index = &it - vector.data();
}
Maxim Egorushkin
  • 131,725
  • 17
  • 180
  • 271
1

Here's a version using an higher-order function. I like it because it's simple to implement and doesn't require you to know the nuances of structured bindings. It also doesn't require any extra dependency.

template <typename Container, typename F>
void enumerate(Container&& c, F&& f)
{
    std::size_t i = 0;
    for(auto&& x : std::forward<Container>(c)) 
    { 
         f(i++, forward_like<Container>(x)); 
    }
}

(Where forward_like moves x if Container is an rvalue.)

Usage:

enumerate(std::vector{'a', 'b', 'c'}, [](auto index, auto x) 
{
    std::cout << index << ": " << x << '\n';
});

Prints:

0: 'a'
1: 'b'
2: 'c'

live example on wandbox.org


C++11 compliant version: live example on wandbox.org

Vittorio Romeo
  • 90,666
  • 33
  • 258
  • 416