To answer the first question, we can start with the second question, which will make the reasons for the first question more obvious.
The construction (called the range-based for, or is based on interval):
for (declaração : expressão) corpo
It is only syntactic sugar and is transformed to the following construction:
{
auto && __range = expressão ;
auto __begin = begin_expr; // normalmente begin_expr é begin(__range)
auto __end = end_expr; // normalmente end_expr é end(__range)
for ( ; __begin != __end; ++__begin) {
declaração = *__begin;
corpo
}
}
Note that this transformation conforms to c++17.
That version of for
works with any expression that can be iterated. In your example, std::vector
can be iterated as it has member functions std::vector::begin
and std::vector::end
, that return iterators to the beginning of the vector and to the end, respectively. For example:
#include <cassert>
int main() {
std::vector<int> v{1, 2, 3};
int soma = 0;
for (int i : v)
soma += i;
assert(6 == soma);
}
If we just take the for
from the above example, we see that
int i
is the statement,
v
is the expression and
soma += i;
is the body.
In the transformation (a little more simplified), we will have:
{
auto __begin = begin(v);
auto __end = end(v);
for ( ; __begin != __end; ++__begin) {
int i = *__begin;
soma += i;
}
}
Notice that the part int i = *__begin
is where each element of the interval (the vector v
in that case) is assigned to the variable i
. The body of for
comes next.
The for
interval-based, As said before, it works with any interval. Therefore, any container that can achieve an iterator of its beginning and end will work with this construction of for
(e. g. arrays fixed-size, some standard library container, such as std::map
, std::vector
, std::array
etc.).
If the concept iterator is unfamiliar, see it as an object that can traverse the elements of a container. For example, a pointer pro first element of the vector can be considered an iterator, because when we increment this pointer, we get access to the elements in sequence of that vector, until we reach the end, which is a pointer to after the last element (meaning the arrival, or end of iteration). In that case, __begin
and __end
would be such iterators, where __begin
has access to the first element (and is incremented during the loop to access the following elements) and __end
represents the end of the iteration. When __begin
is equal to __end
, means the loop ended and all elements were iterated.
Returning to the first question, we see that it is now obvious why we declare vi
as a reference. If we look at your example:
for (auto &vi : v) {
vi *= fator;
}
We have auto &vi
as statement, v
as expression and vi *= fator
as corpo
:
{
auto __begin = begin(v);
auto __end = end(v);
for ( ; __begin != __end; ++__begin) {
auto &vi = *__begin;
vi *= fator;
}
}
When we assign vector elements v
in each iteration, we achieve the reference for that element. If we had the statement as just auto vi
(or double vi
), we would be copying the element to the variable vi
. Since the intention is to modify the element, a copy would not work, we want write access to the element, so the reference is used.
Heed, the use of __
as prefix of names are reserved by standardization. Do not write names like this on your own, as they are used exactly to prevent user code from conflicting with standard library code, syntactic sugars or intrinsic compiler functions.