Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Iteration over std::vector: unsigned vs signed index variable

What is the correct way of iterating over a vector in C++?

Consider these two code fragments, this one works fine:

for (unsigned i=0; i < polygon.size(); i++) {
    sum += polygon[i];
}

and this one:

for (int i=0; i < polygon.size(); i++) {
    sum += polygon[i];
}

which generates warning: comparison between signed and unsigned integer expressions.

I'm new in the world of C++, so the unsigned variable looks a bit frightening to me and I know unsigned variables can be dangerous if not used correctly, so - is this correct?

like image 532
Yuval Adam Avatar asked Jan 03 '09 16:01

Yuval Adam


4 Answers

For iterating backwards see this answer.

Iterating forwards is almost identical. Just change the iterators / swap decrement by increment. You should prefer iterators. Some people tell you to use std::size_t as the index variable type. However, that is not portable. Always use the size_type typedef of the container (While you could get away with only a conversion in the forward iterating case, it could actually go wrong all the way in the backward iterating case when using std::size_t, in case std::size_t is wider than what is the typedef of size_type):


Using std::vector

Using iterators

for(std::vector<T>::iterator it = v.begin(); it != v.end(); ++it) {
    /* std::cout << *it; ... */
}

Important is, always use the prefix increment form for iterators whose definitions you don't know. That will ensure your code runs as generic as possible.

Using Range C++11

for(auto const& value: a) {
     /* std::cout << value; ... */

Using indices

for(std::vector<int>::size_type i = 0; i != v.size(); i++) {
    /* std::cout << v[i]; ... */
}

Using arrays

Using iterators

for(element_type* it = a; it != (a + (sizeof a / sizeof *a)); it++) {
    /* std::cout << *it; ... */
}

Using Range C++11

for(auto const& value: a) {
     /* std::cout << value; ... */

Using indices

for(std::size_t i = 0; i != (sizeof a / sizeof *a); i++) {
    /* std::cout << a[i]; ... */
}

Read in the backward iterating answer what problem the sizeof approach can yield to, though.

like image 174
Johannes Schaub - litb Avatar answered Nov 02 '22 07:11

Johannes Schaub - litb


Four years passed, Google gave me this answer. With the standard C++11 (aka C++0x) there is actually a new pleasant way of doing this (at the price of breaking backward compatibility): the new auto keyword. It saves you the pain of having to explicitly specify the type of the iterator to use (repeating the vector type again), when it is obvious (to the compiler), which type to use. With v being your vector, you can do something like this:

for ( auto i = v.begin(); i != v.end(); i++ ) {
    std::cout << *i << std::endl;
}

C++11 goes even further and gives you a special syntax for iterating over collections like vectors. It removes the necessity of writing things that are always the same:

for ( auto &i : v ) {
    std::cout << i << std::endl;
}

To see it in a working program, build a file auto.cpp:

#include <vector>
#include <iostream>

int main(void) {
    std::vector<int> v = std::vector<int>();
    v.push_back(17);
    v.push_back(12);
    v.push_back(23);
    v.push_back(42);
    for ( auto &i : v ) {
        std::cout << i << std::endl;
    }
    return 0;
}

As of writing this, when you compile this with g++, you normally need to set it to work with the new standard by giving an extra flag:

g++ -std=c++0x -o auto auto.cpp

Now you can run the example:

$ ./auto
17
12
23
42

Please note that the instructions on compiling and running are specific to gnu c++ compiler on Linux, the program should be platform (and compiler) independent.

like image 30
kratenko Avatar answered Nov 02 '22 05:11

kratenko


In the specific case in your example, I'd use the STL algorithms to accomplish this.

#include <numeric> 

sum = std::accumulate( polygon.begin(), polygon.end(), 0 );

For a more general, but still fairly simple case, I'd go with:

#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>

using namespace boost::lambda;
std::for_each( polygon.begin(), polygon.end(), sum += _1 );
like image 43
paxos1977 Avatar answered Nov 02 '22 07:11

paxos1977


Regarding Johannes Schaub's answer:

for(std::vector<T*>::iterator it = v.begin(); it != v.end(); ++it) { 
...
}

That may work with some compilers but not with gcc. The problem here is the question if std::vector::iterator is a type, a variable (member) or a function (method). We get the following error with gcc:

In member function ‘void MyClass<T>::myMethod()’:
error: expected `;' before ‘it’
error: ‘it’ was not declared in this scope
In member function ‘void MyClass<T>::sort() [with T = MyClass]’:
instantiated from ‘void MyClass<T>::run() [with T = MyClass]’
instantiated from here
dependent-name ‘std::vector<T*,std::allocator<T*> >::iterator’ is parsed as a non-type, but instantiation yields a type
note: say ‘typename std::vector<T*,std::allocator<T*> >::iterator’ if a type is meant

The solution is using the keyword 'typename' as told:

typename std::vector<T*>::iterator it = v.begin();
for( ; it != v.end(); ++it) {
...
like image 38
Polat Tuzla Avatar answered Nov 02 '22 06:11

Polat Tuzla