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 << " ";
}
Related
If I declare:
string s = "ARZ";
And then run the following code:
for (auto& c : s) {
cout << (void*)&c << endl;
}
the results will correspond to the addresses of s[0], s[1] and s[2] respectively.
If I remove the & and run:
for (auto c : s) {
cout << (void*)&c << endl;
}
the address of c is always the same.
Presumably c is just a pointer into the vector and it's value advances by sizeof(char) with each loop but I'm finding it hard to get my head round why I'm not required to write *c to access the string char values.
And finally if I run:
for (auto c: s) {
c='?';
cout << c << endl;
}
It prints out 3 question marks.
I'm finding it hard to fathom what c actually is?
In 'for (auto c : str)' what exactly is c?
It's a local variable whose scope is the entire for block and has char type.
for (auto c : str) { loop_statement }
is equivalent to
{
for (auto __begin = str.begin(), __end = str.end(); __begin != __end; ++__begin) {
auto c = *__begin;
loop_statement
}
}
On some implementations, under some conditions, since the lifetime of c ends before the lifetime of next-iteration's c begins, it gets allocated at the same place and gets the same address. You cannot rely on that.
If you don't know the type, you can let the compiler tell you:
#include <string>
template <typename T>
struct tell_type;
int main(){
std::string s = "asdf";
for (auto& c : s) {
tell_type<decltype(c)>();
}
}
Note that there is no definition for tell_type, hence this will result in an error along the line of:
error: implicit instantiation of undefined template 'tell_type<char>'
And similarly
error: implicit instantiation of undefined template 'tell_type<char &>'
for the for (auto& ... loop.
c is char.
The syntax can be misleading until you get it under your skin (but it makes sense).
for (auto c : s) //*distinct object* (think: a copy usually)
for (auto& c : s) //reference into the string (can modify string)
Short: use auto& when you need to modify the contents.
In 'for (auto c : str)' what exactly is c?
c is a local variable with automatic storage within the scope of the range-for statement. It's type will be deduced because you used auto. In case of string str="ARZ";, the deduced type will be char.
Presumably c is just a pointer into the vector
There is no vector, and c is not a pointer. It is a char.
Understanding what range-for does may help. It is equivalent to doing following (the __ prefixed variables are not accessible to the programmer; they are conceptual for the behaviour of the loop):
{
auto && __range = range_expression;
auto __begin = begin_expr;
auto __end = end_expr;
for (; __begin != __end; ++__begin) {
range_declaration = *__begin;
loop_statement
}
}
Or, in this particular case:
{
auto && __range = str;
auto __begin = range.begin();
auto __end = range.end();
for ( ; __begin != __end; ++__begin) {
auto c = *__begin; // note here
cout << (void*)&c << endl;
}
}
Note that if you use auto&, then c will be deduced to be a reference to char. Applying the addressof operator to a reference will not produce the address of the reference variable, but instead the address of the referred object. In this case the referred object would be the character within the string.
In this range-based for loop
for (auto c: s) {c='?'; cout << c << endl;}
there are three iterations because the size of the string s is equal to 3.
Within the loop the assigned value of the object c is ignored and the object is reassigned by the character '?'. So three characters '?' are outputted.
The type of the local variable c is char that is the value type of the class std::string
In this range-based for loop
for (auto& c : s) cout << (void*)&c << endl;
the variable c has a referenced type more precisely the type char &. So in this loop the addresses of the referenced objects are outputted. That is in this loop the addresses of elements of the string s are outputted.
In this range-based for loop
for (auto c : s) cout << (void*)&c << endl;
there is outputted the address of the same local variable c.
When you use references, the reference c is a reference to a character inside the string.
When you don't use references, c is a plain char variable, which contains a copy of the character in the string.
The reason the non-reference variant gives the same pointer for all iterations is simply an implementation detail, where the compiler reuses the space for the variable c inside each iteration.
for (auto& c : s)
c is a reference to a character (char&)
This loop is roughly equivalent to C:
for (char* c=str; *c; ++c)
for (auto c : s)
c is a character (char)
This loop is roughly equivalent to C:
int i=0;
for (char c=str[i]; i<strlen(str); c=str[++i])
I'm wondering if the following is valid:
#include <iostream>
#include <vector>
std::vector<int>& getVec()
{
static std::vector<int> vec{1, 2, 3, 4, 5};
return vec;
}
int main()
{
for (const auto& i : getVec())
{
std::cout << "i = " << i << std::endl;
}
return 0;
}
Basically I'm unsure about the lifetime of the temporary from getVec(). I've looked at both this post and this one, but am in a bit of a different situation as my function returns a reference to static data. Specifically, I'm wondering if scenario violates the following exception in the stated rule:
A temporary bound to a reference parameter in a function call [...]
or if this is indeed safe code. I think it is safe, but just wanted to be sure.
Yes, this is fully valid and well-defined.
The range-based for loop in your question is defined to be equivilent to the following, for imaginary variables range, begin, and end:
auto&& range = getVec();
auto begin = std::begin(range);
auto end = std::end(range);
for (; begin != end; ++begin)
{
const auto& i = *begin;
{
std::cout << "i = " << i << std::endl;
}
}
After reference collapsing rules are applied, the type of range becomes std::vector<int>&. That means no temporaries are ever created. The loop iterates over the static vector defined in getVec.
If getVec instead returned by value, the type of range would be std::vector<int>&&, and lifetime extension would be applied. That would extend the lifetime of the temporary object to that of the reference, and everything would still be totally valid.
When we use an array, it is converted automatically to a pointer to its
first element. (c++ primer 5th ed. pp129)
int ia[3][4];
for (auto p = ia; p!= ia + 3; ++pp){
for (auto q = *p; q ! = *p + 4; ++q)
cout << *q << ' ';
cout << endl;
}
The code snippet above is a good example for the quotation. pis a pointer points to an array of four ints and q is a pointer points to int
However, the for range based loop has different story
for (auto row: ia) // the code won't compile in fact
for (auto col: row)
Here, the type of row is the pointer points to int (reason the second loop won't compile). Why is that? Is this not the case of use the array?
"use an array" is a very handwavy expression.
To understand how the array is used, you must first understand what the range based for loop does. Let's expand your outer loop to use an equivalent regular for loop (I've simplified a little):
{
for (auto __begin = std::begin(ia), __end = std::end(ia);
__begin != __end; ++__begin) {
auto row = *__begin;
for (auto col: row); // oops. Cannot use range-for with a pointer
}
}
The question here is, what will be the deduced type of auto row?
The result of *__begin is an l-value of type "array of 4 ints". auto follows the rules of template argument deduction. An argument cannot be an array object, so auto can never be deduced to be an array. The array type decays to pointer to first element i.e. pointer to int in this case.
An argument can be deduced as "reference to array of 4 ints", so this will work:
for (auto& row: ia)
for (auto col: row)
Your problem here is for (auto row: ia) causes the element of ia to decay to a pointer so row becomes a pointer type. This means you cant use for (auto col: row) since there is no begin function defined for pointers.
What you need to do is take a reference so that you refer to the 1d array and not have a pointer to it. That looks like
for (auto& row: ia) // reference to each row in the array
for (auto col: row) // copy of each element in the row
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.
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.