Access index in range-for loop

I created a preprocessor macro (greatly simplified by @Artyer) that handles this for you in a relatively clean way:

#define for_indexed(...) for_indexed_v(i, __VA_ARGS__)
#define for_indexed_v(v, ...) if (std::size_t v = -1) for (__VA_ARGS__) if ((++v, true))

Example usage:

std::vector<int> v{1, 2, 3};
for_indexed (auto const& item : v) {
    if (i > 0) std::cout << ", ";
    std::cout << i << ": " << item;
}

To use a different loop variable:

for_indexed_v (my_counter, auto const& item : v) ...

The extra control flow logic should be optimized away in any non-debug builds. You're left with a relatively easy-to-read loop syntax.

2020 note: It would probably be more wise to use a lambda-based solution instead of macro trickery. Of course, the syntax wouldn't be as "clean", but it would have the advantage of being recognizable as actual C++ syntax. The choice is yours.

Update 2017/05/28: Made break; statements work correctly
Update 2019/01/28: Put for in the macro name so that the word indexed is a valid variable name. I doubt for_indexed will cause any conflicts.
Update 2020/12/23: Simplified drastically (thanks @Artyer)


Use range-v3. Range-v3 is the next generation range library designed and implemented by ISO C++ Committee member Eric Niebler, and is intended and expected to be merged in the C++ standard in the future.

By Using range-v3 OP's problem can be solved easily:

using ranges::v3::view::zip;
using ranges::v3::view::ints;

for(auto &&[i, idx]: zip(storedValues, ints(0u))){
    std::cout << idx << ": " << i.function() << '\n';
}

You will need a compiler that support C++17 or later to compile this piece of code, not only for the structured binding syntax, but also for the fact that the return type of begin and end function for the return value of ranges::v3::view::zip differ.

You can see the online example here. The documentation of range-v3 is here and the source code itself is hosted here. You can also have a look at here if you are using MSVC compilers.


You can use the enumerate view of range-v3:

std::vector<thisObject> storedValues;
for (auto const& [idx, value] : storedValues | ranges::views::enumerate) {
  std::cout << idx << ": " << value << '\n';
}

C++20 will introduce additional initializations in range-for loops:

std::vector<thisObject> storedValues;
for (size_t idx = 0; auto value : storedValues) {
  std::cout << idx << ": " << value << '\n';
  ++idx;
}

You can't. The index is a specific notion to a vector, and not a generic property of a collection. The range-based loop on the other hand is a generic mechanism for iterating over every element of any collection.

If you do want to use the details of your particular container implementation, just use an ordinary loop:

for (std::size_t i = 0, e = v.size(); i != e; ++i) { /* ... */ }

To repeat the point: Range-based loops are for manipulating each element of any collection, where the collection itself doesn't matter, and the container is never mentioned inside the loop body. It's just another tool in your toolbox, and you're not forced to use it for absolutely everything. By contrast, if you either want to mutate the collection (e.g. remove or shuffle elements), or use specific information about the structure of the collection, use an ordinary loop.

Tags:

C++

C++11