Iterate Through a C++ Vector Using a 'For' Loop

Iterate through a C++ Vector using a 'for' loop

Is there any reason I don't see this in C++? Is it bad practice?

No. It is not a bad practice, but the following approach renders your code certain flexibility.

Usually, pre-C++11 the code for iterating over container elements uses iterators, something like:

std::vector<int>::iterator it = vector.begin();

This is because it makes the code more flexible.

All standard library containers support and provide iterators. If at a later point of development you need to switch to another container, then this code does not need to be changed.

Note: Writing code which works with every possible standard library container is not as easy as it might seem to be.

Iterating C++ vector from the end to the beginning

One way is:

for (vector<my_class>::reverse_iterator i = my_vector.rbegin(); 
i != my_vector.rend(); ++i ) {
}

rbegin()/rend() were especially designed for that purpose. (And yes, incrementing a reverse_interator moves it backward.)

Now, in theory, your method (using begin()/end() & --i) would work, std::vector's iterator being bidirectional, but remember, end() isn't the last element — it's one beyond the last element, so you'd have to decrement first, and you are done when you reach begin() — but you still have to do your processing.

vector<my_class>::iterator i = my_vector.end();
while (i != my_vector.begin())
{
--i;
/*do stuff */

}

UPDATE: I was apparently too aggressive in re-writing the for() loop into a while() loop. (The important part is that the --i is at the beginning.)

Quickest way to iterate in a C++ vector

Why is the algorithm slow?

Currently you're iterating over two vectors, but you're doing nested iteration. Let's call the lengths of the vectors m and n (arbitrarily). Your current algorithm is O(n*m), which is quite slow for large vectors.

If m = n = 1000, in the worst-case (no two elements sum to v), you're doing millions of operations -- whew!

How can I improve the performance?

I can think of one immense speedup: given a vector, a, a vector b, and a desired sum v, you could create a set of numbers from the element-wise difference of v and a and check if any of the values in b are in the set.

The pseudocode is:

if a.empty or b.empty:
return false

set = emptySet

for e in a:
set.add(v - e)

for e in b:
if e in set:
return true

return false

Lookups in an unordered set should be O(1), so this is now an O(max(m, n)) algorithm where m is length of a and n is length of b.

If m = n = 1000, in the worst-case you're now doing thousands of operations -- much better, right?

Implementing this in C++ is left to the reader. :)

Hint: passing by reference is a good idea for vectors.

Iterating by reference on a C++ vector with foreach

It does what you think it does assuming that the signature of do_something is void do_something(int& i).

If you don't put the ampersand in the range-based for-loop you'll get a copy.

How should I loop over the elements of a C++ container in reverse order?

Well, first of all, about your two snippets: Part of the problem is that they're a bit bug prone for actual newbies - the integer underflow, off-by-one in the comparison, forgetting what i signifies and using it as a plain index etc. So I would definitely recommend something else. Also, those snippets may invoke vec.size() many times, which, if the compiler isn't optimizing well enough, would mean a bunch of redundant work.

Option 1: Use iterators

You can reverse-iterate over a container using a pair of iterators (std::rbegin and std::rend, and their constant variants) which represent the reversal of the container's order of elements. Here's what that looks like:

for(auto it = std::crbegin(vec); it != std::crend(vec); it++) {
std::cout << *it << '\n';
}

I made this option the first because it's (mostly) compatible with C++98. We didn't have std::rbegin() and std::crbegin() then, but we did have an rbegin() method for std::vector. std::crbegin() was introduced in C++11

Option 2: Using C++11 (and later) ranged-for loops

You can massage your container - without making a copy of it (although possibly with some payment of time), so that you can use the result in ranger for loop. The answers to this SO question describe several ways to do so, enabling the following code:

auto reverse_view = /* magic involving vec; and not making a copy */
for(auto x : reverse_view) {
std::cout << *it << '\n';
}

They involve either using an "infrastructural" library (namely Boost), or writing a few lines of code which return an iterator pair in an std::pair - which is enough for C++ to use in a ranged-for loop.

Option 3: Using ranged-for and C++20's ranges support

Finally, in C++20, this all becomes easier - with ranges support and std::ranges::reverse_view:

auto reverse_view = std::ranges::reverse_view{vec};
for (const auto& x : reverse_view) {
std::cout << x << '\n';
}

Performance note

Reverse-iterating can in some cases be expensive - because moving backwards, or finding the end of the container, is not always trivial or free. Think of a unidirectional list (where each element comes with a pointer to the next one) - whenever you want to go backwards, you need to traverse the whole list up to your current element to know where the previous element is located. Not all containers are like vectors...

How to iterate over a generic vector

See the error message carefully. It says you're trying to convert a function to iterator. You should call them by adding ().

Change

for (typename vector<T>::iterator iter = v.begin; iter != v.end; iter++) {

to

for (typename vector<T>::iterator iter = v.begin(); iter != v.end(); iter++) {
~~ ~~

For C++ books, The Definitive C++ Book Guide and List

Iterating over vector of custom object with two conditions

You could use std::find_if and work with iterators:

auto it = std::find_if(ItemVector.begin(), ItemVector.end(), 
[&Item](Items *value) {
return value->GetId() == Item.GetId() && !value->GetName().compare(Item.GetName());
}
);

Then you can simply test if it != ItemVector.end() to know if you found something.

There will likely be no (or very small) difference between this and your version in term of speed, but it is a cleaner way to check if something was found or not.

Iterate over multiple vectors, perform operation after n elements

With range-v3, it is simply:

void iterate(const std::vector<int>& a, const std::vector<int>& b, int n)
{
auto r = ranges::view::concat(a, b) | ranges::view::chunk(n);
for (const auto& e : r | ranges::view::bounded) {
foo(e);
}
}

Demo

With for-range available with c++17.

Iterate through vector contained in another class

You could choose to not iterate in main, but instead move the iteration into A:

class A
{
public:
void updateStates(std::function<bool(char)> f)
{
for (const auto& b: b_Storage)
{
updateStatus(b.id, f(b.id));
}
}
};

// ...

int main() {
// ...
A a;
C c;
a.updateStates([&](char id) { return C.getState(id); });
}

This makes main more independent of A's implementation.



Related Topics



Leave a reply



Submit