C ++ (basic): for, references and syntax

8

My teacher introduced this role to us:

void escala(std::vector<double> &v, double fator)

{
    for (auto &vi:v){
    vi *= fator;
    }
}

It serves to multiply all elements of a vector by an already predefined factor.

I have 2 questions:

  

1 . I understood why vector v is passed as a reference in the function argument. However, why 'vi', inside the 'for', is passed as reference as well? (otherwise, it does not work, I already tested it)

     

2 . Can anyone explain to me how this 'for' alternative syntax works? Maybe it's because I do not understand so well that I have the doubt 1. I did not find anything on the internet about, the default syntax 'for (;;)' always appears.

    
asked by anonymous 07.05.2018 / 13:20

2 answers

7

To answer the first question, we can start with the second, which will make the motives of the first more obvious.

The build (called range-based for , or is range-based ):

 for (declaração : expressão) corpo

It is only syntactic sugar and is transformed into the following construct:

{
    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 .

What happens is that this type of for works with any expression that can be iterated. In its example, std::vector can be iterated, since it has the member functions std::vector::begin and std::vector::end , which return iterators to the beginning of the vector and to the end, respectively. For example:

std::vector<int> v{1, 2, 3};
int soma = 0;
for (int i : v) soma += i; // Soma todos os elementos de 'v' em 'soma'.
assert(6 == soma);

If we take only the part of the loop from the previous 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 int i = *__begin is where each element of the range (the v in this case) is assigned to the i variable. The body of the loop comes next.

is interval-based , as said before, works with any range. Therefore, any container that is able to achieve a start and end iterator will work with this construct (eg fixed-length arrays, some standard library containers such as std::map , std::vector , std::array etc.).

If the iterator concept is unfamiliar, see it as an object that can traverse the elements of a container. For example, a pointer to the first vector element can be considered an iterator, because when we increment the pointer, we get access to the elements in sequence from that vector, until it reaches the end, which is a pointer to after the last element (meaning the arrival, or end of the iteration). In this 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 , it means that the loop has ended and all elements have been 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 declaration , 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 the elements of the v vector to each iteration, we get the reference for that element. If we had the statement as only auto vi (or double vi ), we would be copying the element to the vi variable. 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.

  

Warning , the use of __ as a prefix of names is reserved by standardization. Do not write names like this on their own because they are used exactly to prevent user code from conflicting with standard library code, syntactic sugars, or compiler intrinsic functions.

    
07.05.2018 / 15:02
5
  • Because you need to continue to maintain the reference to the object within the code. If you do not use &vi in for , it will not change the original object that was passed by reference, but a copy of it exists within the for only.

  • This construct is called range-based and is used to traverse all items in the array, similar to the foreach command in other languages. To simplify, it would be like saying "for each item of &v , use the variable vi " , that is, in every interaction of for an element will be associated with variable vi , as if it were:

  • vi = &v[0] , vi = &v[1] , and so on.

    More about range-based loop you can read here: link a>

        
    07.05.2018 / 13:40