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

853,499

Solution 1

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.

Solution 2

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.

Solution 3

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 );

Solution 4

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

Solution 5

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).

Share:
853,499
Yuval Adam
Author by

Yuval Adam

λf.(λx.f(x x)) (λx.f(x x))

Updated on July 08, 2022

Comments

  • Yuval Adam
    Yuval Adam almost 2 years

    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?

  • Saulius Žemaitaitis
    Saulius Žemaitaitis over 15 years
    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.
  • Jasper Bekkers
    Jasper Bekkers over 15 years
    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.
  • abelenky
    abelenky over 15 years
    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?)
  • Jay Conrod
    Jay Conrod over 15 years
    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.
  • josesuero
    josesuero over 15 years
    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.
  • Dean Burge
    Dean Burge over 15 years
    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...
  • Johannes Schaub - litb
    Johannes Schaub - litb over 15 years
    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.
  • Steve Jessop
    Steve Jessop over 15 years
    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
    Steve Jessop over 15 years
    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
    Steve Jessop over 15 years
    ... 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.
  • mmx
    mmx over 15 years
    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
  • sharpcodes
    sharpcodes over 15 years
    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.
  • MSalters
    MSalters over 15 years
    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!
  • Admin
    Admin over 15 years
    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.
  • systemsfault
    systemsfault about 13 years
    iteration over arrays using the technique that you have mentioned in this post won't work if the iteration is being performed in a function on an array passed to that function. Because sizeof array will only return the sizeof pointer.
  • kkessell
    kkessell about 13 years
    @user44511: so what is your suggestion?
  • Nils
    Nils over 12 years
    Accodring to this [1] guide using unsinged loop counters is a bad idea. So one should probably cast size first in a signed int. [1] developer.download.nvidia.com/compute/cuda/3_1/toolkit/docs/‌​…
  • Johannes Schaub - litb
    Johannes Schaub - litb over 12 years
    @Nils i agree that using unsigned loop counters is a bad idea. but because the standard library uses unsigned integer types for index and size, i prefer unsigned index types for the standard library. other libraries consequently only use signed types, like the Qt lib.
  • Flexo
    Flexo over 11 years
    C++11 gives you for (auto& val: vec)
  • kratenko
    kratenko over 11 years
    @flexo Thanks, I don't know how I could forget that. Not doing enough C++, I guess. Couldn't believe there's something that practical (thought that was JavaScript syntax, actually). I changed the answer to include that.
  • Siyuan Ren
    Siyuan Ren almost 11 years
    Update for C++11: range based for loop. for (auto p : polygon){sum += p;}
  • Ratata Tata
    Ratata Tata almost 11 years
    Your answer is very nice. It is unpleased that default version of g++ in various OS devkits is under 4.3 which makes it not work.
  • Manu343726
    Manu343726 over 10 years
    @C.R. also update for C++11: Use std::begin() and std::end() to make the array and vector version equal.
  • Jasper Bekkers
    Jasper Bekkers over 10 years
    @pihentagy I guess that would be to set it in the first section of the for-loop. eg. for(auto i = polygon.begin(), end = polygon.end(); i != end; i++)
  • kratenko
    kratenko about 10 years
    @StackedCrooked I posted a C++11-Answer about a year ago, see further down: stackoverflow.com/questions/409348/iteration-over-vector-in-‌​c/…
  • Kuba hasn't forgotten Monica
    Kuba hasn't forgotten Monica almost 10 years
    You should elaborate that this only applies when T is a template argument, and thus the expression std::vector<T*>::iterator is a dependent name. For a dependent name to be parsed as a type, it needs to be prepended by the typename keyword, as the diagnostic indicates.
  • Ben Voigt
    Ben Voigt over 9 years
    @SteveJessop: You can't say with certainty the loop never ends. On the iteration when i == INT_MAX, then i++ causes undefined behavior. At this point anything can happen.
  • Steve Jessop
    Steve Jessop over 9 years
    @BenVoigt: true, and still doesn't provide grounds to ignore the warning :-)
  • sp2danny
    sp2danny over 9 years
    any operation that invalidates iterators, also potentially invalidates end()
  • Bill Cheatham
    Bill Cheatham over 8 years
    Do you need to initialise the vector with std::vector<int> v = std::vector<int>();, or could you have simply used std::vector<int> v; instead?
  • kratenko
    kratenko over 8 years
    @BillCheatham Well - I just tried it out without the initialising, and it did work, so it seems it works without.
  • Bill Cheatham
    Bill Cheatham over 8 years
    Thanks, yes it worked for me to but I wondered if I was just getting lucky with memory and not getting a segfault. I suppose std::vectors shouldn't have the same problems that arrays can...?
  • underscore_d
    underscore_d about 8 years
    I find it strange that you wrote an answer to praise new features of C++11 and yet initialised your container like that instead of using C++11's std::initializer_list, which exists precisely to avoid such boilerplate. Just do this: std::vector<int> v{17, 12, 23, 42};. That avoids the unnecessary default construction from an even more unnecessary temporary and the repeated push_back() - and btw, we should default to using another new C++11 thing, emplace_back(), since at least for more complex objects, push might generate unnecessary copy-constructions.
  • underscore_d
    underscore_d about 8 years
    AFAIK it's recommended to #include <cstddef> rather than <stddef.h> or, worse, the entirety of [c]stdlib, and use std::size_t rather than the unqualified version - and same for any other situation where you have a choice between <cheader> and <header.h>.
  • underscore_d
    underscore_d about 8 years
    @MehrdadAfshari Who cares what "most people" do? "most people" are wrong about lots of things. Post-inc/decrement where the pre value is never used is wrong and inefficient, at least in theory - regardless of how often it's blindly used in sub-par example code everywhere. You shouldn't encourage bad practices just to make things look more familiar to people who don't yet know better.
  • Yibo Yang
    Yibo Yang about 7 years
    if you have to use a loop index and are too lazy to type out the full size_type of the container, consider another C++11 feature decltype, as in for (decltype(vec.size()) i=0; i<vec.size(); ++i)
  • Will
    Will about 7 years
    You don't need the & for the read-only access and I think int would be better than auto here personally: I'd use: for ( int i : v )
  • stackprotector
    stackprotector about 4 years
    @JohannesSchaub-litb The "Using std::vector / Using Range C++11" example looks wrong to me. Where does the "const &" requierement come from? Should the example not be: for(auto i : v) { /* std::cout << i; ... */ }?
  • Vineesh Vijayan
    Vineesh Vijayan over 3 years
    I would like to know ,How the "for (auto& val: vec)" works internally , it is using the iterator internally and begin(), end() functions of the vector class ?