2

I'm working on our company's lib. I see a lot of code like:

std::vector<int>::iterator it = market.vec.begin();
for (size_t i = 0; i < market.vec.size(); ++i)
    it[i] = i + 1;

I think a reference should be better:

std::vector<int>& ref_vec = market.vec;
for (size_t i = 0; i < market.vec.size(); ++i)
    ref_vec[i] = i + 1;

Since if we do

market.vec.resize(20);

somewhere, it will be invalid.

So, is there any benefit that I don't know for using iterator instead of reference?

Thanks.

  • 2
    Related (not dupe): Why do all <algorithm> functions take only ranges, not containers? – user22815 Oct 22 '15 at 2:54
  • 1
    Also, why would you resize a container inside a loop? The purpose of the loop is generally to process each element in the container. If you are not resizing in the loop, you should have no consistency errors. – user22815 Oct 22 '15 at 2:57
  • If you have a new question, you should ask a new question (possibly referencing this one). But changing this question makes it harder for other people to find the answer to the one you have here as they would get all jumbled together. – user40980 Oct 23 '15 at 1:16
2

There a limited containers in the C++ library that are required to be implemented with contiguous memory. vector is one of them, so given a reference to the first element, it is well known where the next element is in memory and the way to get there is also well known.

For other containers, it is not known how it is laid in memory, where all the elements are and how to traverse from one element to the next. Iterators provide that mechanism;

  • Where is the next element (++), possibly where is the previous element (--) and even, get the element 3 places away from the current one (+= 3)
  • It knows how to get to the value of the element being referenced (dereferencing the iterator)
  • It also knows when it gets to the end of the container (when compared to .end())

Once the above semantics are established, it becomes very easy to write general algorithms that are agnostic of the container being used and the memory layout.

The general algorithms can also apply some optimisations knowing the nature of the iterators (random iterators vs. forward iterators) and getting to that iterator type is embedded in the iterator itself. Sure, more specialised algorithms that know the memory layout and can be optimised further are present, but they generally land up being members of the containers.

Note: elements can be accessed by reference with members such as .front(), .back() and .at() etc. as supported by the container. Support for these is defined per container, and the front and back only get to the first and last elements, general algorithm support and usage of these members is limited.

Note on the resize: the call to resize the vector could invalidate both the iterators and element references (depending on the whether a reallocation takes place) and should generally be assumed that the resize does invalidate them.


A common technique when using iterators is to loop from the beginning to the end, given the for loop in your sample, as follows;

// auto used for the sample
// if the container is non-const, then "it" is of type
// std::vector<int>::iterator
for (auto it = market.vec.begin(); it != market.vec.end(); ++it)
    // code

Additionally, indexes etc. could be added as required. Given the code sample, std::iota could be a good replacement for it as well.

std::iota(market.vec.begin(), market.vec.end(), 0);

Giving more consideration to the exact sample code provided, there is little reason to use the iterator or the reference, just access the element in the vector using the index operator []. I don't see any synchronisation code so the vector won't be resized whilst the for loop runs.

That said, favour working with iterators and dereference them to get to the element's value.

  • Your note on resize for references is not valid any more, after I fixed the obvious bug in the OPs code. – Doc Brown Oct 22 '15 at 8:10
  • Noted, I've added clarity on the element references and iterators. – Niall Oct 22 '15 at 8:13
3

Using iterators has several advantages:

  1. It makes your code container-agnostic. The "C++ way" is to accept a pair of iterators. The first points to the first element, the second points to an invalid element (generally one past the final element) with the guarantee that incrementing the "begin" iterator will eventually make it equivalent to the "end" iterator which tells you when you are done processing container data.

  2. In your case you are using a vector, which has an index. What if you were using a different data structure such as a set? You can still iterate a set, but cannot get an index. This is closely related to my previous point.

  3. You can write reusable code to use arbitrary containers with arbitrary ranges. In fact, one could say you could write... algorithms...

0

Your first code example does not really utilize the strengths of an iterator. If one wants to take advantages of an iterator, the code should probably look like this:

std::vector<int>::iterator it = market.vec.begin();
int i=0;
while(it != market.vec.end())
{
    *it = i + 1;
    ++i;
    ++it;
}

Now it is easier to refactor the second part out to a generic function which works on arbitrary iterators, not just vector iterators:

template<class iterator_type>
void AssignIncreasingValues(iterator_type it, iterator_type end)
{
    int i=0;
    while(it != end)
    {
        *it = i + 1;
        ++i;
        ++it;
    } 
}

and call it like this:

    AssignIncreasingValues(market.vec.begin(),market.vec.end());

However, note this has some drawbacks:

  • it is more complex, maybe an overgeneralization
  • as you already spotted by yourself, there should be no resize during the lifetime of your iterators (but is this really a problem?)
  • this does not bring you any real benefit if you need a full random access iterator

So use the first variant (in the form I showed above) if it makes your code more DRY, and the second variant if you have no use case for this kind of generalization - YAGNI principle tells you to use the most simple variant which suffers your needs.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.