I have a vector of objects and am iterating through it using a range-for loop. I am using it to print a function from the object, like this:
vector
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.
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)
You can use the enumerate
view of range-v3:
std::vector<thisObject> storedValues;
for (auto const& [idx, value] : storedValues | ranges::view::enumerate) {
std::cout << idx << ": " << value << '\n';
}
In 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;
}
It's pretty simple honestly, just got to figure out that you can subtract addresses :)
&i will reference the address in memory, and it will increment by 4 from index to index because it's hold a integer from the defined vector type. Now &values[0] references the first point, when you subtract the 2 addresses, the difference between the two will be 0,4,8,12 respectfully, but in reality its subtracting the size of the integer type which is usually 4 bytes. So in correspondence 0 = 0th int,4 = 1st int, 8 = 2nd int, 12 = 3rd int
Here it is in a vector
vector<int> values = {10,30,9,8};
for(auto &i: values) {
cout << "index: " << &i - &values[0];
cout << "\tvalue: " << i << endl;
}
Here it is for a regular array, pretty much the same thing
int values[]= {10,30,9,8};
for(auto &i: values) {
cout << "index: " << &i - &values[0];
cout << "\tvalue: " << i << endl;
}
Note this is for C++11, if you're using g++, remember to use -std=c++11 parameter for compiling
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.
Here's an updated version of the preprocessor macro version for c++17. Clang Generates identical debug and optimized code when compared to writing the loop with an index manually. MSVC generates identical optimized code, but adds a few extra instruction in debug.
#define for_index(...) for_index_v(i, __VA_ARGS__)
#define for_index_v(i, ...) if (size_t i##_next = 0; true) for (__VA_ARGS__) if (size_t i = i##_next++; true)
I was trying to dig into the extra piece of code MSVC was adding in debug builds, I'm not quite sure what it's purpose is. It adds the following at the start of the loop:
xor eax, eax
cmp eax, 1
je $LN5@for_i
Which will skip the loop entirely. Example used: https://godbolt.org/z/VTWhgT