C++11 range based loop: How does it really work - c++

I know how this loop works, and how I can use it in practical problems. But I want to know what is happening under the hood.
I thought that this loop was similar to a regular for loop in which for example
for(int i = 0 ; i < 5 ; i ++){
// instructions
}
Variable i is initialized only once, so I thought that this was the same for range based loops. But if I for example write this code:
for(const int x : vec) {
cout << x << endl;
}
The compiler lets me to do this, but I don't understand how this is possible. If variable x is const, how come in every iteration the x value is different?

Every iteration of the loop creates a local variable x and initializes it to the next element of vec. When the loop iteration ends, x goes out of scope. A single x is never modified.
See this link for the precise semantics.

The range-based for-loop is indeed somewhat different than the classical for-loop in this regard. The declaration you provide (const int x) is declared for every iteration separately, in contrast to classical for-loops.
To be more precise:
for (const int x : vec) {
cout << x << endl;
}
is just a shorthand for (and simply replaced with) the following "classical iterator loop":
for (auto it = vec.begin(), e = vec.end(); it != e; ++it) {
const int x = *it;
cout << x << endl;
}
(except that it and e are not available in the body; also vec is actually "saved" in a separate variable; but let's not focus on unimportant details here; the exact definition of range-based for loop can be looked up here)
Note that const int x is declared and initialized to *it inside the loop body! So it is initialized in every iteration, rather than changed.

For unterstanding purpose you can think of it as if the compiler is replacing for (auto x: y) {...} with for (auto i = begin(y), end = end(y); i != end; ++i) { auto x = *i; {...} }.
For std::vector begin(y)/end(y) will resolve (via the adl) to std::begin(y)/std::end(y) versions that would call y.begin()/y.end() respectively.

Related

C++ idiomatic way of iterating over a container that itself is being modified

I find myself iterating over a container X that is being modified inside the loop. But in each iteration, the unmodified X is needed:
for (int x : X) { // wrong, container X in iteration should be the "original"
modify_X(); // X modified here
}
A solution is to iterate over a copy
containerT X_copy(X);
for (int x : X_copy) {
modify_X();
}
or
for (int x : containerT {X}) {
modify_X();
}
Is there an idiomatic way of doing this?
The last example in your question looks the simplest. However, from C++20, you could also do this:
for (auto copy = X; int i : copy)
{
modify_X();
}
Here's a demo.
Note that in your first snippet, it's not only logically wrong, but it also invokes undefined behavior, as you are modifying a range that you're iterating over.

Iterative for loop and lambda function

I have the following code:
#include <iostream>
#include <algorithm>
struct Point
{
int x, y;
Point(int x, int y): x(x), y(y) {}
};
int main()
{
Point arr[] = {
Point(4,2), Point(0,3), Point(1,2)
};
std::sort(arr, arr+sizeof(arr)/sizeof(Point), [](Point a, Point b){return a.x<b.x;});
return 0;
}
Now, i am supposed to write iterative for loop (built in cpp for_each loop) which prints out all of the elements of the array, where, as a iteration variable we must use auto reference.
Now, this confuses me a bit, since i know this can be done without any iteration variables or something, like this:
std::for_each(arr,arr + sizeof(arr)/sizeof(Point), [](Point a){cout<<a.x<<a.y<<std::endl;}
Obviously, this is not what i am asked to do, so, since i never found myself using iteration variables when dealing with for_each loop, i'd like to find out how am i supposed to do that properly, especially considering the fact that i have to use auto reference. Any help appreciated!
If you're looking for a solution based on std::for_each, you can do the following.
std::for_each(std::begin(arr), std::end(arr),
[](auto& p){ cout << p.x << p.y << "\n"; });
// ^^^^^ auto reference
Here, you have an auto& reference for the object that you intend to do something with in each iteration (in the case above, it would make sense to use const auto&).
This is almost identical to the range-based for loop suggested by #cdhowie. The only interesting point to note here is that std::for_each is one of the few (the only?) exception to the rule that callables passed to STL algorithms must not have side effects. In this case, writing to the global std::cout object is a side effect, and std::for_each explicitly allows that.
You're probably looking for the range-for loop:
for (auto & i : arr) {
std::cout << i.x << ',' << i.y << '\n';
}

Range based for-loop with &

I haven't found a question that answers the part I'm confused on, and I apologize if someone did answer it.
I'm confused on whats going on in this for-loop, how is it looping through the addresses?
int arr[] = { 1, 2, 3, 4, 5 };
for(const int &arrEntry : arr) {
cout << arrEntry << " ";
}
Perhaps the placement of & is causing confusion. Remember that C++ doesn't care where you put spaces. Since this: for (const int &arrEntry : arr) is a declaration of a new variable arrEntry for use inside the loop, the use of & on the left-hand side of its name means we are defining an object that has a reference type, specifically arrEntry is a reference to a const int. This means that within the loop, arrEntry is not a copy of the data you're looping over, only a reference to it. The const means that you can't change its value.
If this were not a declaration, and if arrEntry were defined previously, then the expression &arrEntry would indeed be taking the address of arrEntry. Within the body of the loop, arrEntry is already defined, and so you can take its address with &arrEntry
int arr[] = { 1, 2, 3, 4, 5} ;
for(const int &arrEntry : arr){
cout << arrEntry << " "; // prints a const int
cout << &arrEntry << " "; // prints a pointer to a const int
}
The range-based for loop in C++ is actually just syntactic sugar that is equivalent to the following (provided by cppreference:
for (range_declaration : range_expression) loop_statement;
// is equivalent to:
{
auto && __range = range_expression ;
for (auto __begin = begin_expr, __end = end_expr; __begin != __end; ++__begin) {
range_declaration = *__begin;
loop_statement
}
}
In the above code block, begin_expr and end_expr are equivalent to std::begin(__range) and std::end(__range) respectively.
So in the case of using const int &arrEntry, arrEntry is actually declared inside the "real" (normal) for loop and thus in each iteration it refers to a different object in the range, as if by using the raw iterators directly.
Note that this would not be possible if arrEntry was declared outside the normal for loop, as references cannot be repointed to refer to a different object.
Another important (side) fact to consider is that range_expression is kept alive for the entire duration of the loop, which means you can use a prvalue there (e.g. calling a function that returns a std::vector<int> by value.
In your code, the &arrEntry is a reference to arr. This is implicit in the Ranged based For-Loop.
for(const int &arrEntry : arr){
cout << arrEntry << " ";
}
You could do it without the reference, the result is the same.
But notice the value of arr is copied to arrEntry.
for(const int arrEntry : arr){
cout << arrEntry << " ";
}

Nested Range-based for loop Not working in C++17

I have made nested Range-based for loop program in C++17.
#include <iostream>
#include <vector>
int main()
{
std::vector<int> v = {0, 1, 2, 3, 4, 5};
for (int i : v)
{
for (int a : i)
std::cout << a << ' ';
}
}
GCC genrated an error:
main.cpp: In function 'int main()':
main.cpp:10:22: error: 'begin' was not declared in this scope
for (int a : i)
So,
Why does GCC generate an error for nested range based for loop?
What is the scope of range based for loop?
This problem has nothing to do with nested loops.
The following code snippet is nonsense and the compiler ties itself into knots trying to understand it:
int main() {
std::vector<int> v = {0, 1, 2, 3, 4, 5};
for (int i : v) {
for (int a : i)
std::cout << a << ' ';
}
}
The error message thus is also nonsense. It is like feeding the compiler random characters, and the compiler coming back with "missing ;".
In particular:
for (int i : v)
for (int a : i)
The first line declares i as of type int. How could the second line, then, iterate over an int? int is not an array nor is it user-/library-defined.
Types that can be iterated over are arrays, and user/library defined types with a member begin()/end(), and such with a non-member free function begin()/end() in their namespace, whose begin()/end() return something iterator (or pointer) like.
gcc tried to treat it as an iterable object. It isn't an array. It doesn't have a member begin(). It isn't defined in a namespace containing a non-member begin(). This makes gcc give up at that point, and output a message that it could not find the non-member begin() in a nonsense location (as int has no point of definition).
This will generate the same error:
int i;
for( int a:i );
This line
for (int a : i)
makes no sense. If you read the link on range-based loop you provided, you find that the inner loop would be equivalent to the following code,
{
auto && __range = i ;
auto __begin = begin(__range) ;
auto __end = end(__range) ;
for ( ; __begin != __end; ++__begin) {
a = *__begin;
std::cout << a << ' ';
}
}
The begin and end functions are useful for vectors, maps, ranges etc. because they give iterators. They are also defined by the language for arrays, where they point to the beginning and past the end of the array, so the iterating syntax is the same. They are not defined for a plain int variable.
With this information the produced given by compiler is completely clear: it refers to the absence of begin(i) in the third line of the transformed code. That it is not declared in the scope where the inner loop appears (which is: the outer loop) is just an irrelevant detail at this point, it's not defined anywhere else in the program either.

Auto vs concrete type when iterating over vector?

The book i am reading offers this example when iterating over a vector
for (auto &e: v) {
cout << e << endl;
}
Suppose v is declared as vector<int> v, in other words, we know that the type of elements inside this collection is int.
Is using auto in any way better or preferred to?
for (int &e: v) {
cout << e << endl;
}
Why?
Yes. auto is preferred. Because if you change the declaration ofv from:
std::vector<int> v; //before
to this:
std::vector<float> v; //after
If you use int & in the for, then you have to change that as well. But with auto, no need to change!
In my opinion, working with auto is more or less like programming to interface. So if you do an operation += in the loop, and you don't really care about the type of the loop variable e as long as the type supports += operation, then auto is the solution:
for(auto & e : v)
{
e += 2;
}
In this example, all you care about that the type of e supports += with int on the right hand side. It will work even for user-defined types, which has defined operator+=(int), or operator+=(T) where T is a type which supports implicit conversion from int . It is as if you're programming to interface:
std::vector<Animal*> animals;
animals.push_back(new Dog());
animals.push_back(new Cat());
animals.push_back(new Horse());
for(size_t i = 0 ; i < animals.size(); ++i)
{
animals[i]->eat(food); //program to interface
}
Of course, you would like to write this loop as:
for(Animal * animal : animals)
{
animal->eat(food); //still program to interface
}
Or simply this:
for(auto animal : animals)
{
animal->eat(food); //still program to interface
}
It is still programming to interface.
But at the same time, the point in #David's comment is worth noting.
On your first example, you have less dependency on what the elements of the vector are.
Suppose that in a month, you require that your vector stores larger integers, so you will have to use an std::vector<int64_t>, or some other, wider type. Now all of the code that iterates over that vector is invalid. You'll have to modify each:
for (int &e: v) {}
For a:
for (int64_t &e: v) {}
That is why it's better to just let auto deduce the internal type. That way you can modify the type stored in your vector for another, compatible one, and all your code will still work.