C++ STL – Merit of Using Beginning Iterator Instead of Reference to std::vector

cstl

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.

Best Answer

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.

Related Topic