281

Take the following two lines of code:

for (int i = 0; i < some_vector.size(); i++)
{
    //do stuff
}

And this:

for (some_iterator = some_vector.begin(); some_iterator != some_vector.end();
    some_iterator++)
{
    //do stuff
}

I'm told that the second way is preferred. Why exactly is this?

honk
  • 9,137
  • 11
  • 75
  • 83
Jason Baker
  • 192,085
  • 135
  • 376
  • 510
  • 76
    The second way is preferred is you change `some_iterator++` to `++some_iterator`. Post-increment creates an unnecessary temporary iterator. – jason Jan 03 '10 at 15:21
  • 6
    You should also bring `end()` into the declaration clause. – Lightness Races in Orbit May 31 '11 at 15:11
  • 5
    @Tomalak: anyone using a C++ implementation with an inefficient `vector::end` probably has worse issues to worry about than whether it's hoisted out of loops or not. Personally I prefer clarity - if it was a call to `find` in the termination condition I'd worry, though. – Steve Jessop May 31 '11 at 17:16
  • 13
    @Tomalak: That code is not sloppy (well, the post-increment maybe), it's concise and clear, as far as C++ iterators allow conciseness. Adding more variables adds cognitive effort for the sake of a premature optimization. That's sloppy. – Steve Jessop May 31 '11 at 17:19
  • 2
    @Steve: It's not premature when the compiler is mathematically incapable of doing the optimisation itself. And, IMO, `it != end` is far more clea; it's certainly more concise. Keeping all the references (not `T&`) to the container in one place, too, is tidy. – Lightness Races in Orbit May 31 '11 at 17:22
  • 8
    @Tomalak: it's premature if it's not a bottleneck. Your second point seems absurd to me, since the correct comparison is not between `it != vec.end()` and `it != end`, it's between `(vector::iterator it = vec.begin(); it != vec.end(); ++it)` and `(vector::iterator it = vec.begin(), end = vec.end(); it != end; ++it)`. I don't need to count the characters. By all means prefer one over the other, but other people's disagreement with your preference isn't "sloppiness", it's a preference for simpler code with fewer variables and thus less to think about while reading it. – Steve Jessop May 31 '11 at 17:26
  • 2
    @Steve: "Redundant micro-optimisation", I think, is not the same as "premature optimisation". I'm not calling the methodology "sloppy", Steve, because it's not _my_ methodology; quite the opposite... it's not my methodology because I consider it to be sloppy. :) – Lightness Races in Orbit May 31 '11 at 17:27
  • @Tomalak: It's not the same, but pointless micro-optimization is a subset of premature optimization. If all references to the vector are to be kept together, then I suppose one shouldn't mention the vector in the for loop at all. Rather, write the loop in a function that takes two iterators as parameters. You get the hoist anyway, and a more re-usable loop into the bargain :-) – Steve Jessop May 31 '11 at 17:32
  • 2
    Note that nowadays you should just do `for (auto& item : some_vector) { ...`, which incidentally uses iterators. – Claudiu Dec 13 '15 at 01:35
  • Since C++11 there is [another way](https://stackoverflow.com/a/60338688/2675154) to iterate over a container. – honk Feb 25 '20 at 12:27

27 Answers27

243

The first form is efficient only if vector.size() is a fast operation. This is true for vectors, but not for lists, for example. Also, what are you planning to do within the body of the loop? If you plan on accessing the elements as in

T elem = some_vector[i];

then you're making the assumption that the container has operator[](std::size_t) defined. Again, this is true for vector but not for other containers.

The use of iterators bring you closer to container independence. You're not making assumptions about random-access ability or fast size() operation, only that the container has iterator capabilities.

You could enhance your code further by using standard algorithms. Depending on what it is you're trying to achieve, you may elect to use std::for_each(), std::transform() and so on. By using a standard algorithm rather than an explicit loop you're avoiding re-inventing the wheel. Your code is likely to be more efficient (given the right algorithm is chosen), correct and reusable.

einpoklum
  • 118,144
  • 57
  • 340
  • 684
wilhelmtell
  • 57,473
  • 20
  • 96
  • 131
  • 11
    Also you forgot that iterators can do things like be fail-fast, so that if there is a concurrent modification to the structure you are accessing, you will know about it. You can't do that with just an integer. – Marcin Oct 21 '08 at 22:25
  • 6
    This confuses me: "This is true for vectors, but not for lists, for example." Why? Anyone with a brain will keep a `size_t` member variable keeping track of `size()`. – GManNickG Jul 13 '09 at 07:39
  • 21
    @GMan - in almost all implementations, size() is fast for lists just as much for vectors. Next version of the standard will require this to be true. The real problem is the slowness of retreval by position. – Daniel Earwicker Jul 13 '09 at 07:41
  • 9
    @GMan: Storing the list size requires list slicing and splicing to be O(n) instead of O(1). –  Nov 06 '10 at 15:38
  • 5
    In C++0x, the `size()` member function will be required to have constant time complexity for all containers that support it, including `std::list`. – James McNellis Nov 06 '10 at 16:43
  • 2
    "...making the assumption that the vector has `operator[]()` defined..." Well, that's a very reasonable assumption to make. Did you. by any chance, mean to say "...that the _container_ has..."? – sbi Nov 06 '10 at 21:21
  • and I thought `.size` can always be optimised by extracting out. so I don't see `efficient`. probably `reusable` – zinking Aug 19 '16 at 15:21
  • 1
    I've downvoted this answer because I believe it takes perfectly good, clear, functional code, and pretends it could or should be different. On what grounds? That somebody chose a vector rather than a list (or bicycle or elephant) suggests a vector probably makes most sense. Moreover, they explicitly understand indexing. So then what does using an iterator actually add? That to me is the question, and this answer mostly misses the point. It might as well say "if an elephant were a banana, then..." IMHO Mark Ransom provides a more pragmatic, grounded answer. – omatai Mar 28 '17 at 00:51
  • @omatai I love your comment. I really hate C++ iterators and think they are a pain in my backside. Yes they make using std containers generic, but that is not important to me at all. And what I hate about them is that they are absolutely confusing. Concrete example: std::string has the erase function which accepts an iterator. And thats the end of the story. I don't know the exact type of the iterator. I don't know if I can pass const char* to the iterator. I don't know what type I get if I dereference the iterator. This leads me to have to rely on trial and error. – user13947194 Dec 28 '21 at 20:38
60

It's part of the modern C++ indoctrination process. Iterators are the only way to iterate most containers, so you use it even with vectors just to get yourself into the proper mindset. Seriously, that's the only reason I do it - I don't think I've ever replaced a vector with a different kind of container.


Wow, this is still getting downvoted after three weeks. I guess it doesn't pay to be a little tongue-in-cheek.

I think the array index is more readable. It matches the syntax used in other languages, and the syntax used for old-fashioned C arrays. It's also less verbose. Efficiency should be a wash if your compiler is any good, and there are hardly any cases where it matters anyway.

Even so, I still find myself using iterators frequently with vectors. I believe the iterator is an important concept, so I promote it whenever I can.

Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
  • 2
    C++ iterator's are also horribly broken conceptually. For vectors, I just got caught because the end pointer is acutally end+1(!). For streams the iterator model is just surreal -- an imaginary token that does not exist. Likewise for linked lists. The paradigm only makes sense for arrays, and then not much. Why do I need two iterator object, not just one... – Tuntable Jan 21 '16 at 05:14
  • 6
    @aberglas they're not at all broken, you're just not used to them, which is why I advocate using them even when you don't have to! Half-open ranges are a common concept, and sentinels which are never meant to be accessed directly are about as old as programming itself. – Mark Ransom Jan 21 '16 at 06:25
  • 4
    have a look at the stream iterators and think about what == has been perverted to do in order to fit the pattern, and then tell me iterators are not broken! Or for linked lists. Even for arrays, having to specify one past the end is a broken C style idea -- pointer into the never never. They should be like Java or C# or any other language's iterators, with one iterator required (instead of two objects) and a simple end test. – Tuntable Feb 01 '16 at 09:34
  • @MarkRansom Cpp iterators are crop and I have years of Cpp to back that and feel confident about that statement. I will still use them in for loops; or when I am forced to use them. But being forced doesn't mean they are not bad and unintuitive. They are at least not properly documented on the reference sites, and within your Cpp header files. – user13947194 Dec 28 '21 at 20:49
  • @user13947194 that was my real point - if iterators are not intuitive, you're not using them enough! – Mark Ransom Dec 29 '21 at 00:52
  • @MarkRansom your if statement is wrong, as intuitive does not mean; practice something a million times before you grasp it. Intuitive means that you naturally are able to use the thing. – user13947194 Dec 31 '21 at 22:30
  • @user13947194 intuition is shaped by the patterns you've been exposed to. If something seems intuitive to you, it's because you *have* been exposed to something just like it a million times before. – Mark Ransom Jan 01 '22 at 00:09
  • @MarkRansom which means that everything is intuitive. – user13947194 Jan 01 '22 at 22:01
53

because you are not tying your code to the particular implementation of the some_vector list. if you use array indices, it has to be some form of array; if you use iterators you can use that code on any list implementation.

cruizer
  • 6,103
  • 2
  • 27
  • 34
  • 24
    The std::list interface intentionnaly does not offer operator[](size_t n) because it would be O(n). – MSalters Sep 26 '08 at 11:41
35

Imagine some_vector is implemented with a linked-list. Then requesting an item in the i-th place requires i operations to be done to traverse the list of nodes. Now, if you use iterator, generally speaking, it will make its best effort to be as efficient as possible (in the case of a linked list, it will maintain a pointer to the current node and advance it in each iteration, requiring just a single operation).

So it provides two things:

  • Abstraction of use: you just want to iterate some elements, you don't care about how to do it
  • Performance
asterite
  • 7,761
  • 2
  • 23
  • 18
  • 1
    "it will maintain a pointer to the current node and advance it [good stuff about efficiency]" - yeah, i don't get why people have trouble understanding the concept of iterators. they're conceptually just a superset of pointers. why compute the offset of some element over and over again when you can just cache a pointer to it? well, that's what iterators do too. – underscore_d Feb 24 '16 at 21:56
28

I'm going to be the devils advocate here, and not recommend iterators. The main reason why, is all the source code I've worked on from Desktop application development to game development have i nor have i needed to use iterators. All the time they have not been required and secondly the hidden assumptions and code mess and debugging nightmares you get with iterators make them a prime example not to use it in any applications that require speed.

Even from a maintence stand point they're a mess. Its not because of them but because of all the aliasing that happen behind the scene. How do i know that you haven't implemented your own virtual vector or array list that does something completely different to the standards. Do i know what type is currently now during runtime? Did you overload a operator I didn't have time to check all your source code. Hell do i even know what version of the STL your using?

The next problem you got with iterators is leaky abstraction, though there are numerous web sites that discuss this in detail with them.

Sorry, I have not and still have not seen any point in iterators. If they abstract the list or vector away from you, when in fact you should know already what vector or list your dealing with if you don't then your just going to be setting yourself up for some great debugging sessions in the future.

Chad
  • 2,938
  • 3
  • 27
  • 38
24

You might want to use an iterator if you are going to add/remove items to the vector while you are iterating over it.

some_iterator = some_vector.begin(); 
while (some_iterator != some_vector.end())
{
    if (/* some condition */)
    {
        some_iterator = some_vector.erase(some_iterator);
        // some_iterator now positioned at the element after the deleted element
    }
    else
    {
        if (/* some other condition */)
        {
            some_iterator = some_vector.insert(some_iterator, some_new_value);
            // some_iterator now positioned at new element
        }
        ++some_iterator;
    }
}

If you were using indices you would have to shuffle items up/down in the array to handle the insertions and deletions.

Blacktempel
  • 3,935
  • 3
  • 29
  • 53
Brian Matthews
  • 8,506
  • 7
  • 46
  • 68
  • 4
    if you want to insert elements in the middle of the container then maybe a vector is not a good container choice to begin with. of course, we're back to why iterators are cool; it's trivial to switch to a list. – wilhelmtell Sep 25 '08 at 03:39
  • Iterating over all elements is pretty expensive in a `std::list` compared to a `std::vector`, though, if you're recommending using a linked-list instead of a `std::vector`. See Page 43: http://ecn.channel9.msdn.com/events/GoingNative12/GN12Cpp11Style.pdf In my experience, I've found a `std::vector` is faster than a `std::list` even if I am searching over all of it and removing elements at arbitrary positions. – David Stone Apr 17 '12 at 17:05
  • Indices are stable, so I don't see what additional shuffling is needed for insertions and deletions. – musiphil Mar 27 '13 at 21:11
  • ...And with a linked list - which is what should be in use here - your loop statement would be `for (node = list->head; node != NULL; node = node->next)` which is shorter than your first two lines of code put together (declaration and loop head). So I say again - there isn't much fundamental difference in brevity between using iterators and not using them - you still have satisfy the three parts of a `for` statement, even if you use `while`: declare, iterate, check termination. – Engineer Nov 15 '15 at 12:54
16

Separation of Concerns

It's very nice to separate the iteration code from the 'core' concern of the loop. It's almost a design decision.

Indeed, iterating by index ties you to the implementation of the container. Asking the container for a begin and end iterator, enables the loop code for use with other container types.

Also, in the std::for_each way, you TELL the collection what to do, instead of ASKing it something about its internals

The 0x standard is going to introduce closures, which will make this approach much more easy to use - have a look at the expressive power of e.g. Ruby's [1..6].each { |i| print i; }...

Performance

But maybe a much overseen issue is that, using the for_each approach yields an opportunity to have the iteration parallelized - the intel threading blocks can distribute the code block over the number of processors in the system!

Note: after discovering the algorithms library, and especially foreach, I went through two or three months of writing ridiculously small 'helper' operator structs which will drive your fellow developers crazy. After this time, I went back to a pragmatic approach - small loop bodies deserve no foreach no more :)

A must read reference on iterators is the book "Extended STL".

The GoF have a tiny little paragraph in the end of the Iterator pattern, which talks about this brand of iteration; it's called an 'internal iterator'. Have a look here, too.

xtofl
  • 40,723
  • 12
  • 105
  • 192
15

Because it is more object-oriented. if you are iterating with an index you are assuming:

a) that those objects are ordered
b) that those objects can be obtained by an index
c) that the index increment will hit every item
d) that that index starts at zero

With an iterator, you are saying "give me everything so I can work with it" without knowing what the underlying implementation is. (In Java, there are collections that cannot be accessed through an index)

Also, with an iterator, no need to worry about going out of bounds of the array.

cynicalman
  • 5,861
  • 3
  • 30
  • 30
  • 2
    I don't think "object oriented" is the correct term. Iterators aren't "object oriented" in design. They promote functional programming more than object oriented programming, because they encourage separating the algorithms from the classes. – wilhelmtell Sep 25 '08 at 03:14
  • Also, iterators don't help avoiding getting out of bounds. Standard algorithms do, but iterators alone don't. – wilhelmtell Sep 25 '08 at 03:15
  • Fair enough @wilhelmtell, I'm obviously thinking of this from a Java-centric point of view. – cynicalman Sep 25 '08 at 03:18
  • 1
    And I think that it does promote OO, because it is separating operations on collections from the implementation of that collection. A collection of objects shouldn't necessarily know what algorithms should be used to work with them. – cynicalman Sep 25 '08 at 03:19
  • Actually there are versions of the STL out there that have checked iterators, meaning that it will throw some sort of out-of-bounds exception when you try to do something with that iterator. – Dominik Grabiec Sep 25 '08 at 06:52
15

Aside from all of the other excellent answers... int may not be large enough for your vector. Instead, if you want to use indexing, use the size_type for your container:

for (std::vector<Foo>::size_type i = 0; i < myvector.size(); ++i)
{
    Foo& this_foo = myvector[i];
    // Do stuff with this_foo
}
underscore_d
  • 6,309
  • 3
  • 38
  • 64
Pat Notz
  • 208,672
  • 30
  • 90
  • 92
  • 1
    @Pat Notz, that is a very good point. In the course of porting an STL-based Windows application to x64, I have had to deal with hundreds of warnings about assigning size_t to an int possibly causing truncation. – bk1e Sep 25 '08 at 05:28
  • 1
    Not to mention the fact that the size types are unsigned and int is signed, so you have non-intuitive, bug-hiding conversions going on just to compare `int i` to `myvector.size()`. – Adrian McCarthy Jul 08 '15 at 18:24
15

Another nice thing about iterators is that they better allow you to express (and enforce) your const-preference. This example ensures that you will not be altering the vector in the midst of your loop:


for(std::vector<Foo>::const_iterator pos=foos.begin(); pos != foos.end(); ++pos)
{
    // Foo & foo = *pos; // this won't compile
    const Foo & foo = *pos; // this will compile
}
Pat Notz
  • 208,672
  • 30
  • 90
  • 92
  • This looks reasonable, but I still doubt that if that is the reason for having `const_iterator`. If I alter the vector in the loop, I do it for a reason, and for 99.9% of the time that altering is not an accident, and for the rest, it is just a bug like any kind of bugs in the code the author needs to fix. Because in Java, and many other languages, there is no const object at all, but users of those languages never have a problem with no const support in those languages. – neevek Nov 27 '16 at 15:30
  • 2
    @neevek If that isn't the reason for having `const_iterator`, then what on Earth could the reason be? – underscore_d May 11 '17 at 10:23
  • @underscore_d, I am wondering too. I am not expert on this, it is just that the answer is not convincible to me. – neevek May 11 '17 at 10:35
12

I probably should point out you can also call

std::for_each(some_vector.begin(), some_vector.end(), &do_stuff);

MSalters
  • 173,980
  • 10
  • 155
  • 350
8

STL iterators are mostly there so that the STL algorithms like sort can be container independent.

If you just want to loop over all the entries in a vector just use the index loop style.

It is less typing and easier to parse for most humans. It would be nice if C++ had a simple foreach loop without going overboard with template magic.

for( size_t i = 0; i < some_vector.size(); ++i )
{
   T& rT = some_vector[i];
   // now do something with rT
}
'
Jeroen Dirks
  • 7,705
  • 12
  • 50
  • 70
5

I don't think it makes much difference for a vector. I prefer to use an index myself as I consider it to be more readable and you can do random access like jumping forward 6 items or jumping backwards if needs be.

I also like to make a reference to the item inside the loop like this so there are not a lot of square brackets around the place:

for(size_t i = 0; i < myvector.size(); i++)
{
    MyClass &item = myvector[i];

    // Do stuff to "item".
}

Using an iterator can be good if you think you might need to replace the vector with a list at some point in the future and it also looks more stylish to the STL freaks but I can't think of any other reason.

Adam Pierce
  • 33,531
  • 22
  • 69
  • 89
  • _most_ algorithms operate once on each element of a container, sequentially. Of course there are exceptions in which you'd want to traverse a collection in a specific order or manner, but in this case I'd try hard and write an algorithm that integrates with the STL and that works with iterators. – wilhelmtell Sep 25 '08 at 03:30
  • This would encourage reuse and avoid off-by-one errors later on. I'd then call that algorithm just like any other standard algorithm, with iterators. – wilhelmtell Sep 25 '08 at 03:32
  • 1
    Don't even need advance(). The iterator has the same += and -= operators as an index (for vector and vector-like containers). – MSalters Sep 26 '08 at 11:42
  • `I prefer to use an index myself as I consider it to be more readable` only in some situations; in others, indices quickly get very messy. `and you can do random access` which is not a unique feature of indices at all: see http://en.cppreference.com/w/cpp/concept/RandomAccessIterator – underscore_d May 11 '17 at 10:29
4

After having learned a little more on the subject of this answer, I realize it was a bit of an oversimplification. The difference between this loop:

for (some_iterator = some_vector.begin(); some_iterator != some_vector.end();
    some_iterator++)
{
    //do stuff
}

And this loop:

for (int i = 0; i < some_vector.size(); i++)
{
    //do stuff
}

Is fairly minimal. In fact, the syntax of doing loops this way seems to be growing on me:

while (it != end){
    //do stuff
    ++it;
}

Iterators do unlock some fairly powerful declarative features, and when combined with the STL algorithms library you can do some pretty cool things that are outside the scope of array index administrivia.

Jason Baker
  • 192,085
  • 135
  • 376
  • 510
  • The truth is that if all iterators were as compact as your final example, right out of the box, I'd have little issue with them. Of course, that's actually equal to `for (Iter it = {0}; it != end; ++it) {...}` - you just left out the declaration - so brevity is not much different from your second example. Still, +1. – Engineer Nov 15 '15 at 12:46
3

The second form represents what you're doing more accurately. In your example, you don't care about the value of i, really - all you want is the next element in the iterator.

Colen
  • 13,428
  • 21
  • 78
  • 107
3

Indexing requires an extra mul operation. For example, for vector<int> v, the compiler converts v[i] into &v + sizeof(int) * i.

HugoTeixeira
  • 4,674
  • 3
  • 22
  • 32
Marc Eaddy
  • 1,762
  • 3
  • 16
  • 23
  • Probably not a significant disadvantage relative to iterators in most cases, but it is a good thing to be aware of. – Brent Bradburn Mar 01 '10 at 04:33
  • 3
    For isolated single element accesses, probably. But if we're talking about loops - like the OP was - then I'm pretty sure this answer is based on an imaginary non-optimising compiler. Any half-decent one will have ample opportunity and likelihood to cache the `sizeof` and just add it once per iteration, rather than doing the whole offset calculation again every time. – underscore_d Feb 24 '16 at 22:03
2

During iteration you don't need to know number of item to be processed. You just need the item and iterators do such things very good.

Sergey Stolyarov
  • 2,587
  • 3
  • 27
  • 40
2

No one mentioned yet that one advantage of indices is that they are not become invalid when you append to a contiguous container like std::vector, so you can add items to the container during iteration.

This is also possible with iterators, but you must call reserve(), and therefore need to know how many items you'll append.

danpla
  • 655
  • 5
  • 9
2

If you have access to C++11 features, then you can also use a range-based for loop for iterating over your vector (or any other container) as follows:

for (auto &item : some_vector)
{
     //do stuff
}

The benefit of this loop is that you can access elements of the vector directly via the item variable, without running the risk of messing up an index or making a making a mistake when dereferencing an iterator. In addition, the placeholder auto prevents you from having to repeat the type of the container elements, which brings you even closer to a container-independent solution.

Notes:

  • If you need the the element index in your loop and the operator[] exists for your container (and is fast enough for you), then better go for your first way.
  • A range-based for loop cannot be used to add/delete elements into/from a container. If you want to do that, then better stick to the solution given by Brian Matthews.
  • If you don't want to change the elements in your container, then you should use the keyword const as follows: for (auto const &item : some_vector) { ... }.
honk
  • 9,137
  • 11
  • 75
  • 83
1

I don't use iterators for the same reason I dislike foreach-statements. When having multiple inner-loops it's hard enough to keep track of global/member variables without having to remember all the local values and iterator-names as well. What I find useful is to use two sets of indices for different occasions:

for(int i=0;i<anims.size();i++)
  for(int j=0;j<bones.size();j++)
  {
     int animIndex = i;
     int boneIndex = j;


     // in relatively short code I use indices i and j
     ... animation_matrices[i][j] ...

     // in long and complicated code I use indices animIndex and boneIndex
     ... animation_matrices[animIndex][boneIndex] ...


  }

I don't even want to abbreviate things like "animation_matrices[i]" to some random "anim_matrix"-named-iterator for example, because then you can't see clearly from which array this value is originated.

AareP
  • 2,355
  • 3
  • 21
  • 28
  • I don't see how indices are any better in this sense. You could easily use iterators and just pick a convention for their names: `it`, `jt`, `kt`, etc. or even just continue using `i`, `j`, `k`, etc. And if you need to know exactly what an iterator represents, then to me something like `for (auto anim = anims.begin(); ...) for (auto anim_bone = anim->bones.begin(); ...) anim_bone->wobble()` would be more descriptive than having to continually index like `animation_matrices[animIndex][boneIndex]`. – underscore_d May 11 '17 at 10:32
  • wow, it feels like ages ago when I wrote that opinion. nowadays using both foreach and c++ iterators without cringing much. I guess working with buggy code for years builds up one's tolerance, so it's easier to accept all syntaxes and conventions... as long as it works, and as long as one can go home you know ;) – AareP May 12 '17 at 19:19
  • Haha, indeed, I didn't really look at how old this was before! Something else I somehow didn't think of last time was that nowadays we also have the range-based `for` loop, which makes the iterator-based way of doing this even more concise. – underscore_d May 12 '17 at 20:52
1

Several good points already. I have a few additional comments:

  1. Assuming we are talking about the C++ standard library, "vector" implies a random access container that has the guarantees of C-array (random access, contiguos memory layout etc). If you had said 'some_container', many of the above answers would have been more accurate (container independence etc).

  2. To eliminate any dependencies on compiler optimization, you could move some_vector.size() out of the loop in the indexed code, like so:

    const size_t numElems = some_vector.size();
    for (size_t i = 0; i 
  3. Always pre-increment iterators and treat post-increments as exceptional cases.

for (some_iterator = some_vector.begin(); some_iterator != some_vector.end(); ++some_iterator){ //do stuff }

So assuming and indexable std::vector<> like container, there is no good reason to prefer one over other, sequentially going through the container. If you have to refer to older or newer elemnent indexes frequently, then the indexed version is more appropropriate.

In general, using the iterators is preferred because algorithms make use of them and behavior can be controlled (and implicitly documented) by changing the type of the iterator. Array locations can be used in place of iterators, but the syntactical difference will stick out.

user22044
  • 216
  • 1
  • 3
1
  • If you like being close to the metal / don't trust their implementation details, don't use iterators.
  • If you regularly switch out one collection type for another during development, use iterators.
  • If you find it difficult to remember how to iterate different sorts of collections (maybe you have several types from several different external sources in use), use iterators to unify the means by which you walk over elements. This applies to say switching a linked list with an array list.

Really, that's all there is to it. It's not as if you're going to gain more brevity either way on average, and if brevity really is your goal, you can always fall back on macros.

Engineer
  • 8,529
  • 7
  • 65
  • 105
0

Both the implementations are correct, but I would prefer the 'for' loop. As we have decided to use a Vector and not any other container, using indexes would be the best option. Using iterators with Vectors would lose the very benefit of having the objects in continuous memory blocks which help ease in their access.

Messiah
  • 99
  • 7
  • 2
    "Using iterators with Vectors would lose the very benefit of having the objects in continuous memory blocks which help ease in their access." [citation needed]. Why? Do you think that an increment of an iterator to a contiguous container can't be implemented as a simple addition? – underscore_d Feb 24 '16 at 22:07
0

Even better than "telling the CPU what to do" (imperative) is "telling the libraries what you want" (functional).

So instead of using loops you should learn the algorithms present in stl.

Cyber Oliveira
  • 8,178
  • 4
  • 28
  • 18
0

I always use array index because many application of mine require something like "display thumbnail image". So I wrote something like this:

some_vector[0].left=0;
some_vector[0].top =0;<br>

for (int i = 1; i < some_vector.size(); i++)
{

    some_vector[i].left = some_vector[i-1].width +  some_vector[i-1].left;
    if(i % 6 ==0)
    {
        some_vector[i].top = some_vector[i].top.height + some_vector[i].top;
        some_vector[i].left = 0;
    }

}
johnsyweb
  • 136,902
  • 23
  • 188
  • 247
Krirk
  • 1,466
  • 2
  • 10
  • 10
0

For container independence

John Topley
  • 113,588
  • 46
  • 195
  • 237
all2one
  • 387
  • 2
  • 3
  • 9
0

I felt that none of the answers here explain why I like iterators as a general concept over indexing into containers. Note that most of my experience using iterators doesn't actually come from C++ but from higher-level programming languages like Python.

The iterator interface imposes fewer requirements on consumers of your function, which allows consumers to do more with it.

If all you need is to be able to forward-iterate, the developer isn't limited to using indexable containers - they can use any class implementing operator++(T&), operator*(T) and operator!=(const &T, const &T).

#include <iostream>
template <class InputIterator>
void printAll(InputIterator& begin, InputIterator& end)
{
    for (auto current = begin; current != end; ++current) {
        std::cout << *current << "\n";
    }
}

// elsewhere...

printAll(myVector.begin(), myVector.end());

Your algorithm works for the case you need it - iterating over a vector - but it can also be useful for applications you don't necessarily anticipate:

#include <random>

class RandomIterator
{
private:
    std::mt19937 random;
    std::uint_fast32_t current;
    std::uint_fast32_t floor;
    std::uint_fast32_t ceil;

public:
    RandomIterator(
        std::uint_fast32_t floor = 0,
        std::uint_fast32_t ceil = UINT_FAST32_MAX,
        std::uint_fast32_t seed = std::mt19937::default_seed
    ) :
        floor(floor),
        ceil(ceil)
    {
        random.seed(seed);
        ++(*this);
    }

    RandomIterator& operator++()
    {
        current = floor + (random() % (ceil - floor));
    }

    std::uint_fast32_t operator*() const
    {
        return current;
    }

    bool operator!=(const RandomIterator &that) const
    {
        return current != that.current;
    }
};

int main()
{
    // roll a 1d6 until we get a 6 and print the results
    RandomIterator firstRandom(1, 7, std::random_device()());
    RandomIterator secondRandom(6, 7);
    printAll(firstRandom, secondRandom);

    return 0;
}

Attempting to implement a square-brackets operator which does something similar to this iterator would be contrived, while the iterator implementation is relatively simple. The square-brackets operator also makes implications about the capabilities of your class - that you can index to any arbitrary point - which may be difficult or inefficient to implement.

Iterators also lend themselves to decoration. People can write iterators which take an iterator in their constructor and extend its functionality:

template<class InputIterator, typename T>
class FilterIterator
{
private:
    InputIterator internalIterator;

public:
    FilterIterator(const InputIterator &iterator):
        internalIterator(iterator)
    {
    }

    virtual bool condition(T) = 0;

    FilterIterator<InputIterator, T>& operator++()
    {
        do {
            ++(internalIterator);
        } while (!condition(*internalIterator));

        return *this;
    }

    T operator*()
    {
        // Needed for the first result
        if (!condition(*internalIterator))
            ++(*this);
        return *internalIterator;
    }

    virtual bool operator!=(const FilterIterator& that) const
    {
        return internalIterator != that.internalIterator;
    }
};

template <class InputIterator>
class EvenIterator : public FilterIterator<InputIterator, std::uint_fast32_t>
{
public:
    EvenIterator(const InputIterator &internalIterator) :
        FilterIterator<InputIterator, std::uint_fast32_t>(internalIterator)
    {
    }

    bool condition(std::uint_fast32_t n)
    {
        return !(n % 2);
    }
};


int main()
{
    // Rolls a d20 until a 20 is rolled and discards odd rolls
    EvenIterator<RandomIterator> firstRandom(RandomIterator(1, 21, std::random_device()()));
    EvenIterator<RandomIterator> secondRandom(RandomIterator(20, 21));
    printAll(firstRandom, secondRandom);

    return 0;
}

While these toys might seem mundane, it's not difficult to imagine using iterators and iterator decorators to do powerful things with a simple interface - decorating a forward-only iterator of database results with an iterator which constructs a model object from a single result, for example. These patterns enable memory-efficient iteration of infinite sets and, with a filter like the one I wrote above, potentially lazy evaluation of results.

Part of the power of C++ templates is your iterator interface, when applied to the likes of fixed-length C arrays, decays to simple and efficient pointer arithmetic, making it a truly zero-cost abstraction.

Marcus Harrison
  • 819
  • 6
  • 19