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

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.

Related

How to access elements of an array from a function [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 1 year ago.
Improve this question
The foreach loop type doesn't work if the array being iterated has come from a parameter and was declared in another scope.
// C++ program to demonstrate use of foreach
#include <iostream>
using namespace std;
void display(int arr[])
{
for (int x : arr)
cout << x << endl;
}
int main()
{
int arr[] = { 10, 20, 30, 40 };
display(arr);
}
Here's what the error looks like:
What are begin and end?
What is happening behind the scenes that gives this error?
Is the for loop implicitly changed to an iterator declaration before running? is this because C style arrays are not container objects and therefore don't have begin and end? If this is true then why does this code work:
#include <iostream>
using namespace std;
int main()
{
int arr[] = { 10, 20, 30, 40 };
for (int x : arr)
cout << x << endl;
}
Is there some information decaying happening when passed as an argument?
That for loop is called a range-based for loop. The compiler is transforming that code into something else. You can see all the details here https://en.cppreference.com/w/cpp/language/range-for (it's different for C++11, C++17, and C++20).
However, in principle, your code becomes this:
{
auto && __range = arr;
auto __begin = begin_expr;
auto __end = end_expr;
for ( ; __begin != __end; ++__begin) {
x = *__begin;
cout << x << endl;
}
}
The thing here is that begin_expr and end_expr can be different things. If arr was an array, like int arr[3], __range would be __range + 3. If arr was a class with members begin() and end() it would be __range.begin(). Otherwise, it would be begin(__range).
This last case is were your code is landing, because arr is not an array but a pointer to int (int [] arr means int* arr). And there is no begin() and end() for int*.
It works here, because in this example, arr is an array of 4 integers:
int arr[] = { 10, 20, 30, 40 };
for (int x : arr)
cout << x << endl;
There are different ways to do this. This is how I'd prefer to do it myself:
template <typename T>
void display(std::vector<T> const & arr)
{
for (int x : arr)
cout << x << endl;
}
int main()
{
std::vector<int> arr { 10, 20, 30, 40 };
display(arr);
}
You can check https://cppinsights.io/ to see how the compiler transforms your code (to some degree). For instance, your code becomes this (mind that it does not work if the code does not compile, so I had to comment out the loop):
#include <iostream>
using namespace std;
void display(int * arr) // <-- notice int*
{
}
int main()
{
int arr[4] = {10, 20, 30, 40}; // <-- notice int[4]
display(arr);
}
And if you take this code:
int main()
{
int arr[] = { 10, 20, 30, 40 };
for (int x : arr)
cout << x << endl;
}
the result is this:
int main()
{
int arr[4] = {10, 20, 30, 40};
{
int (&__range1)[4] = arr;
int * __begin1 = __range1;
int * __end1 = __range1 + 4L;
for(; __begin1 != __end1; ++__begin1)
{
int x = *__begin1;
std::cout.operator<<(x).operator<<(std::endl);
}
}
}
The first piece of code doesn't work, because
void display(int arr[])
is actually equivalent to
void display(int *arr)
and you can't use a single pointer for iteration, because it doesn't have any length information. See also What is array to pointer decay.
The second piece of code works because the range-expression in a range-based for must be
any expression that represents a suitable sequence (either an array or an object for which begin and end member functions or free functions are defined, see below) or a braced-init-list.
Source
So instead of the non-existing member functions arr.begin() and arr.end() it can use the free functions std::begin() and std::end() which are overloaded for arrays.
first of all, let me suggest you to edit your question as #Ted mentions in his comment.
I must admit the error message is not quite intuitive. The appearance of missing begin and end stems from the definition of range-based for loop. As you can see, you're passing an array of unknown size as the range_expression, right? Linked page has further to say about this case:
If range_expression is an expression of array type, then begin_expr is
__range and end_expr is (__range + __bound), where __bound is the number of elements in the array (if the array has unknown size or is
of an incomplete type, the program is ill-formed)
Your second code snippet actually uses an array with known size, since its use is in the same scope as its initialization. Array initialization states:
When initializing an array of unknown size, the largest subscript for
which an initializer is specified determines the size of the array
being declared.
Let me offer you a standard library template for better grip on arrays. It is .. err .. std::array :). What you get is basically value semantics and consistence with other types:
accepting argument by value makes a copy of the whole content,
accepting argument by (const) reference has a familiar syntax,
you don't implicitly lose the size information when passing an array object.
So your code will happen to look like this:
#include <array>
#include <iostream>
using namespace std;
void display(const array<int>& arr)
{
for (int x : arr)
cout << x << endl;
}
int main()
{
std::array arr{ 10, 20, 30, 40 };
display(arr);
}
When you give a C-style array to a function, you need to give its size as well because the function is not aware of the array's size. The array argument int x[] is considered as a pointer on the array's first element (thus like an int* x). In consequence, the function can't tell how big the array is as it only has the pointer (the address of the first element). You need to specify how many int (or other types) from the provided adress the function shall read to obtain valid elements.
#include <iostream>
using namespace std;
void display(int arr[], int nsize)
{
int i=0;
while (i < nsize){
cout << *arr << endl;
// you move the pointer to the next element to be read
arr++;
i++;
}
}
int main()
{
int arr[] = { 10, 20, 30, 40 };
int n= sizeof(arr)/sizeof(int);
display(arr, 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 << " ";
}

Why must use reference in ranged-based for loops

I was stuck in a piece of code in C++ Primer and had thought about it more than 1 hour. The code is
for (auto row : ia)//should use &row here
for (auto col : row)
The explanation for it is
We do so in order to avoid the normal array to pointer conversion. Because row is not a reference, when the compiler
initializes row it will convert each array element (like any other object of array type)
to a pointer to that array’s first element. As a result, in this loop the type of row is
int*. The inner for loop is illegal. Despite our intentions, that loop attempts to
iterate over an int*.
I know it has something to do with iterations each time doing for(auto col:row). What I can't understand about
We do so in order to avoid the normal array to pointer conversion"
is what's the form of "ia" we passed in for the outer loop? Should it be a pointer that points to the address of its first element rather than a "concrete" array? Then what's wrong for the inner loop? I thought it should be the same mechanism with the outer loop.. I can't understand the posts on Q&A. Someone please enlighten me please...What's wrong with my understanding...A good link is also welcomed! Many thanks in advance!
The declaration for ia is
constexpr size_t rowCnt = 3, colCnt = 4;
int ia[rowCnt][colCnt]; // 12 uninitialized elements
// for each row
for (size_t i = 0; i != rowCnt; ++i) {
// for each column within the row
for (size_t j = 0; j != colCnt; ++j) {
// assign the element's positional index as its value
ia[i][j] = i * colCnt + j;
}
}
In general, a range-based for loop declared as:
for ( decl : coll ) {
statement }
is equivalent to the following, if coll provides begin() and end() members:
for (auto _pos=coll.begin(), _end=coll.end(); _pos!=_end; ++_pos )
{
decl = *_pos;
statement
}
or, if that doesn’t match, to the following by using a global begin() and end() taking coll as argument:
for (auto _pos=begin(coll), _end=end(coll); _pos!=_end; ++_pos )
{
decl = *_pos;
statement
}
Now look at the line decl = *_pos; , here each time compiler will initialize dec , it will convert each array element (like any other object of array type) to a pointer to that array’s first element .
In your case , type of raw will comes out to be int* on which next for loop can't work and hence it becomes illegal .
Range-based for loops are based on the begin and end functions which don't work on T* but work on T[N].
The implementation for:
for ( range_declaration : range_expression ) loop_statement
is along the lines of:
{
auto && __range = range_expression ;
for (auto __begin = begin_expr,
__end = end_expr;
__begin != __end; ++__begin) {
range_declaration = *__begin;
loop_statement
}
}
Since in the outer for loop the array would decay to a pointer with auto row, the inner for loop would become illegal.
Range based for loop works for either with array type or user defined type having member functions begin and end.
By array-to-pointer decay rule when you pass array to a function which takes a pointer, then the array decay to pointer
But when you use a template by **reference** then template type is deduced as a array type:
template<typename T>
void foo(T&);
int main() {
int ia[2][2] = {{0,1}, {2,3}};
auto row_val = ia[0]; //int*
auto& row_ref = ia[0]; //int [2]
foo(row_val); //void foo<int*>(int*&)
foo(row_ref); //void foo<int [2]>(int (&) [2])
}
Here you can see that (from the error it creates!!) that type deduced for row_val and row_ref are not same
row_val ==> int*
row_ref ==> int [2]
You have to use for(auto& row: ia) as row will now array type not a pointer. hence you can use the inner loop for (auto col : row).

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

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.

C++11 range-based for on a vector of pointers

I have just compiled GCC 4.6.0, and I wanted to try the new features out, starting with the range-based for loop.
The first loop I wanted to change was iterating on a std::vector of pointers. I changed the code to use the new syntax, but it didn't compile.
I have tried to substitute another for loop, which was on a std::vector of structs, and it compiled and ran perfectly.
Here is a short test code to show you my problem:
#include <vector>
#include <iostream>
int main()
{
std::vector< int > values;
values.push_back(2);
values.push_back(5);
values.push_back(8);
values.push_back(13);
values.push_back(17);
for (int &n : values)
{
std::cout << n << "\n";
}
std::vector< int* > pointers;
pointers.push_back(new int(2));
pointers.push_back(new int(5));
pointers.push_back(new int(8));
pointers.push_back(new int(13));
pointers.push_back(new int(17));
for ((int*) &p : values)
{
std::cout << (*p) << "\n";
}
for( unsigned int i = 0; i < pointers.size(); ++i)
{
delete pointers[i];
}
return 0;
}
When I try to compile it (yes, I give -std=c++0x as a parameter to g++), it dies with this error:
main.cpp|27|error: found ‘:’ in nested-name-specifier, expected ‘::’
If I comment the lines 27-30 out, it's OK.
What am I doing wrong? Isn't the pointer-reference declaring syntax right?
Or is there a limitation of contained types where range-based for loops can be used?
for ((int*) &p : values)
This is wrong. (int*) is an expression alone, so you need to do int*& (with no parenthesis, that makes an expression - aka "not a type name") at least to make it correct. I prefer to use auto or auto&, personally.
You can do :
for (auto p : values) // here p is a pointer, a copy of each pointer
or
for (auto& p : values ) // here p is a non-const reference to a pointer
or
for ( int* p : values ) // here p is a copy of each pointer
or in generic code:
for ( auto&& p: values ) // p is either a const reference to what is in values, or a non-const reference, depends on the context
I think you meant to iterate over 'pointers' instead of 'values' there...