277

I am new to the C++ language. I have been starting to use vectors, and have noticed that in all of the code I see to iterate though a vector via indices, the first parameter of the for loop is always something based on the vector. In Java I might do something like this with an ArrayList:

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

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

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Flynn
  • 5,903
  • 7
  • 38
  • 55
  • 1
    The for loop isn't a function, so it doesn't have parameters (or arguments, which is what you pass in). Do you mean something like `std::vector::size_type i = 0;`, though, or perhaps `std::vector::iterator it = vector.begin();`? – chris Oct 03 '12 at 05:53
  • Exactly, all of the examples that I see are written like that. – Flynn Oct 03 '12 at 05:55
  • 5
    In Java, I would prefer a for-each loop or use iterators. Pretty much same as C++ although slightly different syntax. – Jesse Good Oct 03 '12 at 05:56
  • Buy a book and read up on std::vector before you start using it. The STL has iterators which does exactly what you want. There are also algorithms (for_each) which helps you iterate without a for loop. – user93353 Oct 03 '12 at 05:56
  • 2
    possible duplicate of [Why use iterators instead of array indices?](http://stackoverflow.com/questions/131241/why-use-iterators-instead-of-array-indices) – johnsyweb Oct 03 '12 at 06:00
  • 16
    Most of the answers here incorrectly assume the Q to be : *What is the best/shortest way to iterate over `std::vector`?*, the actual Q being asked here is: *Is there any reason I don't see thisin C++? Is it bad practice?* aka *Why do I always see code in C++ which uses iterators while iterating over `std::vector`?* – Alok Save Oct 03 '12 at 06:16
  • Fix your vector.size() outside the loop and you're computationally equal to the iterator method. In fact, if you want to do more with an integer pointer except for vector opps, use your method. – LovaBill Feb 21 '13 at 10:50
  • 1
    I think a better explanation is given in this question http://stackoverflow.com/questions/409348/iteration-over-vector-in-c – rabin Jan 30 '14 at 06:34
  • @chris: `The for loop isn't a function, so it doesn't have parameters` No kidding. `Do you mean something like std::vector::size_type i = 0` No, he doesn't mean anything like that. He means indexing into a vector, where the index increases. – stackoverflowuser2010 Aug 28 '21 at 17:39

14 Answers14

223

The reason why you don't see such practice is quite subjective and cannot have a definite answer, because I have seen many of the code which uses your mentioned way rather than iterator style code.

Following can be reasons of people not considering vector.size() way of looping:

  1. Being paranoid about calling size() every time in the loop condition. However either it's a non-issue or it can be trivially fixed
  2. Preferring std::for_each() over the for loop itself
  3. Later changing the container from std::vector to other one (e.g. map, list) will also demand the change of the looping mechanism, because not every container support size() style of looping

C++11 provides a good facility to move through the containers. That is called "range based for loop" (or "enhanced for loop" in Java).

With little code you can traverse through the full (mandatory!) std::vector:

vector<int> vi;
...
for(int i : vi) 
  cout << "i = " << i << endl;
iammilind
  • 68,093
  • 33
  • 169
  • 336
  • 33
    Just to note a small disadvantage of _range based for loop_: you cannot use it with `#pragma omp parallel for`. – liborm May 03 '14 at 18:58
  • 2
    I like the compact version because there's less code to read. Once you make the mental adjustment it's much easier to understand and bugs stand out more. It also makes it much more obvious when there's a non-standard iteration happening because there's a much bigger chunk of code. – Code Abominator May 11 '16 at 04:23
  • 2
    @liborm note that this is no longer true; OpenMP 5.0 supports range-based iteration. (source: https://stackoverflow.com/a/51390846/2378475) – djsavvy Mar 08 '22 at 19:53
  • 1
    The range based for loop syntax is nice and concise. However, debugging may call for the explicit knowledge of the index. For example: a file is written by the loop. It is found that line 125 corresponding to the 125th iteration produces incorrect values. How do you breakpoint the loop at the 125th iteration for debug? (You do not know the value of the loop variable at the failing iteration without changing the code and printing that explicitly as well. – Matyas Jul 16 '22 at 18:01
161

The cleanest way of iterating through a vector is via iterators:

for (auto it = begin (vector); it != end (vector); ++it) {
    it->doSomething ();
}

or (equivalent to the above)

for (auto & element : vector) {
    element.doSomething ();
}

Prior to C++0x, you have to replace auto by the iterator type and use member functions instead of global functions begin and end.

This probably is what you have seen. Compared to the approach you mention, the advantage is that you do not heavily depend on the type of vector. If you change vector to a different "collection-type" class, your code will probably still work. You can, however, do something similar in Java as well. There is not much difference conceptually; C++, however, uses templates to implement this (as compared to generics in Java); hence the approach will work for all types for which begin and end functions are defined, even for non-class types such as static arrays. See here: How does the range-based for work for plain arrays?

Community
  • 1
  • 1
JohnB
  • 13,315
  • 4
  • 38
  • 65
  • 7
    auto, free begin/end are also C++11. And too, you should use ++it, instead of it++ in many cases. – ForEveR Oct 03 '12 at 05:55
  • Yes, you're right. Implementing `begin` and `end`, however, is a one-liner. – JohnB Oct 03 '12 at 06:03
  • @JohnB it is a more-than-one-liner, because it works for fixed size arrays too. `auto` on the other hand would be quite tricky. – juanchopanza Oct 03 '12 at 06:06
  • If you need it for vector only it's a one-liner. – JohnB Oct 03 '12 at 06:06
  • Still, the first example is misleading, since it cannot work in C++03, whereas your phrasing suggests that it does. – juanchopanza Oct 03 '12 at 06:31
  • If you change to another collection with a *simple* iterator, sure, but switch to one of the map classes and suddenly the iterator is a pair. IME those changes are more common, someone discovers that their array or vector is sparse and ... everything changes. – Code Abominator May 11 '16 at 04:22
  • One more detail concerning `begin` and `end`, they need the `std::` prefix unless you add `using namespace std;`. Therefore(?) many prefer `v.begin()` and `v.end()` over `begin(v)` etc. – Max Dec 24 '20 at 07:48
135

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.

sifferman
  • 2,955
  • 2
  • 27
  • 37
Alok Save
  • 202,538
  • 53
  • 430
  • 533
  • 35
    Could anyone please explain me why in this particular case/code snippet you advise iterators over indexing? What is this "flexibility" you're talking about? Personally, I don't like iterators, they bloat the code - simply more characters to type for the same effect. Especially if you can't use `auto`. – Violet Giraffe Oct 03 '12 at 06:57
  • 8
    @VioletGiraffe: While using iterators it is hard to go wrong with certain cases like empty ranges and the code is more verbose.Ofcourse its a matter or perception and choice, So it can be debated endlessly. – Alok Save Oct 03 '12 at 07:08
  • 25
    Why do you only show how to declare the iterator but not how to use it to do the loop...? – underscore_d Nov 19 '19 at 16:41
49

The right way to do that is:

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

Where T is the type of the class inside the vector. For example if the class was CActivity, just write CActivity instead of T.

This type of method will work on every STL (Not only vectors, which is a bit better).

If you still want to use indexes, the way is:

for(std::vector<T>::size_type i = 0; i != v.size(); i++) {
    v[i].doSomething();
}
Dig
  • 3,850
  • 3
  • 27
  • 33
  • 1
    isn't `std::vector::size_type` always `size_t`? That's the type I always use for it. – Violet Giraffe Oct 03 '12 at 06:53
  • 1
    @VioletGiraffe I'm pretty sure you are right (haven't really checked), but it's a better practice to use std::vector::size_type. – Dig Oct 04 '12 at 13:41
18

Using the auto operator really makes it easy to use as one does not have to worry about the data type and the size of the vector or any other data structure

Iterating vector using auto and for loop

vector<int> vec = {1,2,3,4,5}

for(auto itr : vec)
    cout << itr << " ";

Output:

1 2 3 4 5

You can also use this method to iterate sets and list. Using auto automatically detects the data type used in the template and lets you use it. So, even if we had a vector of string or char the same syntax will work just fine

Hrishikesh
  • 356
  • 2
  • 11
12

A correct way of iterating over the vector and printing its values is as follows:

#include<vector>

// declare the vector of type int
vector<int> v;

// insert elements in the vector
for (unsigned int i = 0; i < 5; ++i){
    v.push_back(i);
}

// print those elements
for (auto it = v.begin(); it != v.end(); ++it){
    std::cout << *it << std::endl;
}

But at least in the present case it is nicer to use a range-based for loop:
for (auto x: v) std::cout << x << "\n";
(You may also add & after auto to make x a reference to the elements rather than a copy of them. It is then very similar to the above iterator-based approach, but easier to read and write.)

Mario Petrovic
  • 7,500
  • 14
  • 42
  • 62
Nikhil Rai
  • 129
  • 1
  • 4
  • 1
    why use `auto` instead of `int`? –  May 08 '21 at 15:01
  • @Harith The variable ```it``` is an iterator for the integer vector ```v```, not an integer, so we cannot use ```int``` to declare ```it```, but we can use ```auto``` to let the compiler automatically figure out the type of ```it```. You can also explicitly declare the type of ```it``` in the for loop using ```vector::iterator it``` like ```for (vector::iterator it = v.begin(); it != v.end(); ++it)```. As for ```x```, you can technically use ```int``` to declare it, however the ```auto``` specifier is often used in range-based for loops for automatic type deduction. – YuanLinTech Apr 14 '23 at 08:22
8

There's a couple of strong reasons to use iterators, some of which are mentioned here:

Switching containers later doesn't invalidate your code.

i.e., if you go from a std::vector to a std::list, or std::set, you can't use numerical indices to get at your contained value. Using an iterator is still valid.

Runtime catching of invalid iteration

If you modify your container in the middle of your loop, the next time you use your iterator it will throw an invalid iterator exception.

Eddie Parker
  • 4,770
  • 3
  • 35
  • 43
  • 1
    could you point to some we article/post that explains the above points with example code? would be great! or if you could add one :) – Anu Jan 13 '19 at 13:20
  • 1. IIRC, using (actually, dereferencing) an invalid iterator is UB (ouch!). While _some_ implementations MAY throw exceptions - IIRC, it is far from universal. – No-Bugs Hare Jan 21 '21 at 10:39
  • 2. Certain modifications to certain containers in the middle of the loop are perfectly fine (for example, in list<>/map<>/etc.) – No-Bugs Hare Jan 21 '21 at 10:40
7

Here is a simpler way to iterate and print values in vector.

for(int x: A) // for integer x in vector A
    cout<< x <<" "; 
Akram Mohammed
  • 141
  • 1
  • 8
5

With STL, programmers use iterators for traversing through containers, since iterator is an abstract concept, implemented in all standard containers. For example, std::list has no operator [] at all.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
ForEveR
  • 55,233
  • 2
  • 119
  • 133
5

I was surprised nobody mentioned that iterating through an array with an integer index makes it easy for you to write faulty code by subscripting an array with the wrong index. For example, if you have nested loops using i and j as indices, you might incorrectly subscript an array with j rather than i and thus introduce a fault into the program.

In contrast, the other forms listed here, namely the range based for loop, and iterators, are a lot less error prone. The language's semantics and the compiler's type checking mechanism will prevent you from accidentally accessing an array using the wrong index.

Diomidis Spinellis
  • 18,734
  • 5
  • 61
  • 83
5

don't forget examples with const correctness - can the loop modify the elements. Many examples here do not, and should use cont iterators. Here we assume

class T {
  public:
    T (double d) : _d { d } {}
    void doSomething () const { cout << _d << endl; return; }
    void changeSomething ()   { ++_d; return; }
  private:
    double _d;
};

vector<T> v;
// ...
for (const auto iter = v.cbegin(); iter != v.cend(); ++iter) {
    iter->doSomething();
}

Note also, that with the C++11 notation, the default is to copy the element. Use a reference to avoid this, and/or to allow for original elements to be modified:

vector<T> v;
// ...
for (auto t : v) {
    t.changeSomething(); // changes local t, but not element of v
    t.doSomething();
}
for (auto& t : v) {      // reference avoids copying element
    t.changeSomething(); // changes element of v
    t.doSomething();
}
for (const auto& t : v) { // reference avoids copying element
    t.doSomething();      // element can not be changed
}
2
 //different declaration type
    vector<int>v;  
    vector<int>v2(5,30); //size is 5 and fill up with 30
    vector<int>v3={10,20,30};
    
    //From C++11 and onwards
    for(auto itr:v2)
        cout<<"\n"<<itr;
     
     //(pre c++11)   
    for(auto itr=v3.begin(); itr !=v3.end(); itr++)
        cout<<"\n"<<*itr;
bashar
  • 21
  • 3
1
int main()
{
    int n;
    int input;
    vector<int> p1;
    vector<int> ::iterator it;

    cout << "Enter the number of elements you want to insert" << endl;
    cin >> n;

    for (int i = 0;i < n;i++)
    {
        cin >> input;
        p1.push_back(input);
    }
    for(it=p1.begin();it!=p1.end();it++)
    {
        cout << *it << endl;
    }
      //Iterating in vector through iterator it

    return 0;
}

conventional form of iterator

Shujaul Hind
  • 105
  • 1
  • 9
-4

If you use

std::vector<std::reference_wrapper<std::string>> names{ };

Do not forget, when you use auto in the for loop, to use also get, like this:

for (auto element in : names)
{
    element.get()//do something
}
rob
  • 13
  • 4