views:

6294

answers:

10

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?

+1  A: 

Your polygon.size() returns an unsigned. The compiler complains when you comapre it with int i.

gimel
It doesn't, it returns size_t. Assuming size() returns unsigned might be valid on your specific platform.
Jasper Bekkers
This depends on the STL library (i.e. compiler set) that you are using and also whether you're compiling 32 or 64 bit. Use size_t.
use size_t - the compiler error message specifies unsigned, your knowledge of std::vector translates to size_t.
gimel
+4  A: 

Use size_t :

for (size_t i=0; i < polygon.size(); i++)

Quoting Wikipedia:

The stdlib.h and stddef.h header files define a datatype called size_t which is used to represent the size of an object. Library functions that take sizes expect them to be of type size_t, and the sizeof operator evaluates to size_t.

The actual type of size_t is platform-dependent; a common mistake is to assume size_t is the same as unsigned int, which can lead to programming errors, particularly as 64-bit architectures become more prevalent.

Igor Oks
size_t OK for vector, as it must store all objects in an array (itself an object, too) but a std::list may contain more than size_t elements!
MSalters
size_t is normally sufficient to enumerate all bytes in address space of a process. While I can see how this may not be the case on some exotic architectures, I'd rather not worry about it.
Arkadiy
+6  A: 

A call to vector<T>::size() returns a value of type std::vector<T>::size_type, not int, unsigned int or otherwise.

Also generally iteration over a container in C++ is done using iterators, like this.

std::vector<T>::iterator i = polygon.begin();
std::vector<T>::iterator end = polygon.end();

for(; i != end; i++){
    sum += *i;
}

Where T is the type of data you store in the vector.

Or using the different iteration algorithms (std::transform, std::copy, std::fill, std::for_each et cetera).

Jasper Bekkers
Iterators are generally a good idea, though i doubt there's a need to store "end" in a separate variable and it can all be done inside a for(;;) statement.
frgtn
I know begin() and end() are amortized constant time, but I generally find this to be more readable than to cram everything into one line.
Jasper Bekkers
You can split the for into separate lines to improve readability. Declaring iterators outside the loop means you need a different iterator name for every loop over containers of different types.
Jay Conrod
I am aware of all the differences, and what it basically comes down to is personal preference; this is generally how I end up doing things.
Jasper Bekkers
it is generally recommended you store the result of end() in a variable to avoid the repeated function call. even if it's constant time, there is still overhead in making the call. there is no reason to initialize outside the loop and the only result of that is cluttering the scope.
+2  A: 
for (vector<int>::iterator it = polygon.begin(); it != polygon.end(); it++)
    sum += *it;
Mehrdad Afshari
For vector this is fine, but generically it's better to use ++it rather than it++, in case the iterator itself is non-trivial.
Steve Jessop
Personally, I am used to using ++i, but I think most people prefer i++ style (the default VS code snippet for "for" is i++). Just a thought
Mehrdad Afshari
+2  A: 

A bit of history:

To represent whether a number is negative or not computer use a 'sign' bit. int is a signed data type meaning it can hold positive and negative values (about -2billion to 2billion). Unsigned can only store positive numbers (and since it doesn't waste a bit on metadata it can store more: 0 to about 4billion).

std::vectore::size() returns a unsigned, for how could a vector have negative length?

The warning is telling you that the right operand of your inequality statement can hold more data then the left.

Essentially if you have a vector with more then 2 billion entries and you use a integer to index into you'll hit overflow problems (the int will wrap back around to negative 2 billion).

ecoffey
A: 

The first is type correct, and correct in some strict sense. (If you think about is, size can never be less than zero.) That warning strikes me as one of the good candidates for being ignored, though.

Charlie Martin
I think it's a terrible candidate to be ignored - it's easy to fix, and once in a while genuine bugs occur due to errors comparing signed/unsigned values inappropriately. For instance in this case, if the size is greater than INT_MAX the loop never terminates.
Steve Jessop
... or maybe it terminates immediately. One of the two. Depends whether the signed value is converted to unsigned for comparison, or the unsigned is converted to signed. On a 64bit platform with a 32bit int, though, like win64, the int would be promoted to size_t, and the loop never ends.
Steve Jessop
+25  A: 

Iterating Backwards

See this answer.

Iterating Forwards

This 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 indices

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

Using arrays

Using iterators

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

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.

Johannes Schaub - litb
size type of pointers: using difference_type might be more portable. try iterator_traits<element_type*>::difference_type. this is one mouthful of a declaration, but it is more portable...
wilhelmtell
wilhelmtell, for what should i use difference_type? sizeof is defined to return size_t :) i don't understand you. if i were to subtract pointers from each other, difference_type would be the right choice.
Johannes Schaub - litb
+2  A: 

I usually use BOOST_FOREACH:

#include <boost/foreach.hpp>

BOOST_FOREACH( vector_type::value_type& value, v ) {
    // do something with 'value'
}

It works on STL containers, arrays, C-style strings, etc.

Martin Cote
Good answer to some other question (how should I iterate a vector?), but completely not at all what the OP was asking (what is the meaning of the warning about an unsigned variable?)
abelenky
Well, he asked what the correct way of iterating over a vector was. So seems relevant enough. The warning is just why he's not happy with his current solution.
jalf
+7  A: 

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 );
ceretullis
+1 for functional programming :-)
Steve Jessop
A: 

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) {
...
Polat Tuzla