recently I've encountered very very peculiar question when using auto in C++, just ... just look at the following code snippet :
my main function:
#include <list>
#include <iostream>
#include <stdio.h>
int main(){
int a = 10, b = 20, c = 30;
list<int> what;
what.push_back(a);
what.push_back(b);
what.push_back(c);
read(what);
return 0;
}
And here's function read:
void read(const list<int>& con){
for (auto it : con){
printf("%p\n", &it);
cout << it << endl;
}
return ;
}
And here's is the output :
0x7fffefff66a4
10
0x7fffefff66a4
20
0x7fffefff66a4
30
What the heck is that? Same address with different content !?
And more strange this is, if I modify the for-loop by adding an '&'
that is:
for (auto& it : con){
All the output makes sense immediately, the addresses would change by iteration
So my question is,
Why does the '&' sign make a change under this circumstance?
for (auto it : con){
Same address with different content !?
This is very typical for variables with automatic storage duration. This has nothing to do with auto in C++†. You would get the same result if you had used int:
for (int it : con){
The lifetime of it (as well as each automatic variable within the loop) is just a single iteration. Since the lifetime of the it in last iteration was ended, the next iteration can re-use the same memory, and that's why the address is the same.
Why does the '&' sign make a change under this circumstance?
Because T& declares a reference to type T. Reference variables are different from non-references (object variables). Instead of holding a value such as an object would, a reference instead "refers" to another object.
When you use the addressof operator on a reference, the result will be the address of the referred object; not the address of the reference (which might not even have an address, since it's not an object). That is why the address changes in the latter case. In this case, the references would refer to the int objects that are stored in the nodes of what (because con itself is a reference, and refers to the passed object).
† I mention in C++, because in C auto is in fact a storage class modifier that signifies automatic storage class. It has never had that meaning in standard C++, and its use obsolete in C as well. It's a vestigial keyword from the B language.
In C++, auto declares a type that will be deduced from context.
let's see the expanded version of the : loop syntax first.
for( auto it: container) {
...
}
is conceptually the same as
for( auto _it = container.begin(); _it != container.end(); it++) {
auto it = *_it;
...
}
while the reference form:
for( auto& it: container)
is the same as
for( auto _it = container.begin(); _it != container.end(); it++) {
auto &it = *_it;
...
}
So in the first case it is a copy of the items in the container, in the second case it is a (lvalue) reference of it, hence if you modify it in the second loop it affects the items in the container
The address issue too can be explained this way: in the copy example the local variable has always the same address in each loop iteration (because their lifetime do not overlap, the compiler has no reason not to use the same address in the stack), thought if you factorize the code inside a function you may observe it changing in different function invocation (because the stack size might be different), in the reference example the address is different every time, because taking the address of a reference will yield the address of the object being referenced (in this case, the item in the container)
Note that auto is standing in for int in your case. So it's a red herring. Consider
for (int i = 0; i < 10; ++i){
int j = i;
cout << (void*)&j << '\n';
}
Since j has automatic storage duration, it is most likely created each time with the same address - but points to a different value - , j is being pushed then popped from a stack on each iteration (let's set aside compiler optimisations). That is what is happening in your case with for (auto it : con){. it has automatic storage duration.
When you write
for (auto& it : con){
it is a reference to an int within the container con, so its address will differ on each iteration.
Related
I'm trying to better understand the interactions of lambda expressions and iterators.
What is the difference between these three snippets of code? onSelect is an std::function that is called when a component is selected.
Example 1 and 3 seem to work quite nicely. Example 2 returns the same index value, regardless of the component clicked.
My intuition is that Example 2 only results in one symbol being generated, and therefore the function only points to the first value. My question is, why would for_each result in multiple function definitions being generated, and not the normal for loop?
components[0].onSelect = [&]{ cout<<0; };
components[1].onSelect = [&]{ cout<<1; };
components[2].onSelect = [&]{ cout<<2; };
components[3].onSelect = [&]{ cout<<3; };
//And so on
vs
for (int i = 0; i < numComponents; ++i)
{
components[i].onSelect = [&]
{
cout<<components[i];
};
}
vs
int i = 0;
std::for_each (std::begin (components), std::end (components), [&](auto& component)
{
component.onSelect = [&]{
cout<<i;
});
What is the difference between these three snippets of code?
Well, only the first one is legal.
My intuition is that Example 2 only results in one symbol being generated
Each lambda expression generates a unique unnamed class type in the smallest enclosing scope. You have one block scope (inside the for loop) and one lambda expression so yes, there's only one type.
Each instance of that type (one per iteration) could differ in state, because they could all capture different values of i. They don't, though, they all capture exactly the same lexical scope by reference.
and therefore the function only points to the first value
A lambda expression is always a class type, not a function. A lambda expression with an empty capture is convertible to a free function pointer - but you don't have an empty capture. Finally, the lambda didn't capture only the first value - or any value - but an unusable reference to the variable i. Because you explicitly asked to capture by reference ([&]) instead of value.
That is, they all get the same reference to i whatever its particular value at the time they're instantiated, and i will have been set to numComponents and then gone out of scope before any of them can be invoked. So, even if it hadn't gone out of scope, referring to components[i] would almost certainly be Undefined Behaviour. But as it has gone out of scope, it is a dangling reference. This is an impressive density of bugs in a small amount of code.
Compare the minimal change:
for (int i = 0; i < numComponents; ++i) {
components[i].onSelect = [i, &components]
{
cout<<components[i];
};
}
which captures i by value, which is presumably what you really wanted, and only takes components by reference. This works correctly with no UB.
My question is, why would for_each result in multiple function definitions being generated, and not the normal for loop?
You have two nested lambda expressions in example 3, but we're only concerned with the inner one. That's still a single lambda expression in a single scope, so it's only generating one class type. The main difference is that the i to which it has (again) captured a reference, has presumably not gone out of scope by the time you try calling the lambda.
For example, if you actually wrote (and a minimal reproducible example would have shown this explicitly)
int i = 0;
std::for_each (std::begin (components), std::end (components), [&](auto& component)
{
component.onSelect = [&]{
cout<<i;
});
for (i = 0; i < numComponents; ++i)
components[i].onSelect();
then the reason it would appear to work is that i happens to hold the expected value whenever you call the lambda. Each copy of it still has a reference to the same local variable i though. You can demonstrate this by simply writing something like:
int i = 0;
std::for_each (std::begin (components), std::end (components), [&](auto& component)
{
component.onSelect = [&]{
cout<<i;
});
components[0].onSelect();
components[1].onSelect();
i = 2;
components[1].onSelect();
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.
I created a map of type map<T, const T&>. For current example purpose, let say T is:
class Bar {
public:
Bar(int x) {this->x = x;}
int x;
};
Next I create a map and insert Bar keyed with some integers.
Bar bs[] = {Bar(1), Bar(2), Bar(3)};
map<int, const Bar&> my_map;
for (int i = 0; i < 3; i++) {
const Bar &b = bs[i];
cout << "Setting map." << i
<< " with x = " << b.x << endl ;
my_map.insert(std::make_pair(i, b));
}
So far everything looks good, and b.x prints the values 1; 2; 3 as expected. Next we retrieve these values back.
for (int i = 0; i < 3; i++) {
auto iter = my_map.find(i);
if (iter == my_map.end()) {
cout << "Not found!" << endl;
continue;
}
cout << "map." << i << " = " << iter->second.x << endl;
}
The output prints the last value each time as shown below.
// map.0 = 3
// map.1 = 3
// map.2 = 3
And that's what is confusing to me, as I expect 1; 2; 3. If I replace value type of map with just const Bar it gives 1; 2; 3. I've been trying to make sense out of it, but so far it just looks like undefined behaviour to me. The wildest explanation I can imagine is that &b is like a box storing pointer to the object, and the box ends up being shared across loop, and make_pair uses &b as a box value than like a pointer/reference (and hence explains the last value being printed).
Edit: I understand it may not be good idea to use map like this, but I'm curious why this is happening than what should I be using instead. As in semantically, what did I miss when I wrote this and why it went through compiler, or why compiler made whatever assumption it made.
Edit: Example on repl.it running the code: https://repl.it/repls/IgnorantExhaustedBluejay
Essentially the same problem as here: How can I have a pair with reference inside vector?
Your call to std::make_pair creates a temporary std::pair object that does not have a reference as its second member. The second member of the pair is a regular value of type Bar. Meanwhile, your map stores references. The reference gets bound to the second member of the temporary created by std::make_pair. Later the temporary gets destroyed. The reference becomes dangling.
Each temporary on each iteration of the cycle is apparently created at the same location in memory. So, all these dangling references in your map refer to the same location in memory. Which just happens to hold the residual value of 3 at the time of printing. That explains the output.
A map with raw references is not a very good idea. But if you want to somehow force it to work with raw references, stop using std::make_pair. Instead, manually construct a proper std::pair, making sure to explicitly specify the proper types
my_map.insert(std::pair<const int, const Bar &b>(i, b));
Or you can keep using std::make_pair as follows
my_map.insert(std::make_pair(i, std::cref(b)));
But switching entirely to std::reference_wrapper and std::cref is a better idea.
P.S. BTW, in C++17 mode GCC refuses to compile the code with raw references. C++14 mode does compile it.
I wasn't even aware that it's possible to have a map of references
You should probably simply store the object you want directly :
map<int, Bar> my_map;
If you want the "Bar"s objects to live outside the map, you should use pointers instead of references. Just be sure you don't destruct the Bar objects without removing them from the map :
map<int, Bar*> my_map;
my_map[2] = &bs[0];
and then:
int x = my_map[2]->x;
Edit
I think the map is holding a reference to the temporary pair. You can see this in debug if you extract the creation of the pair :
auto tempPair = std::make_pair(i, b);
my_map.insert(tempPair);
Then after adding bs[0] if we run the creation of the pair, the value of my_map[0] change even before adding the second one:
This makes it work:
my_map.insert(std::make_pair(i, std::reference_wrapper<const Bar>(b)));
I apologize if this question has already been answered (I tried searching around, but couldn't find anything quite the same, and similar questions' solutions didn't work), but how do I pass an object (in this case, a vector of objects) and have the function edit those values without returning anything?
Example:
void incVector(std::vector<int> vec)
{
for (auto l = 0; l < int(vec.size()); l++)
{
vec[l]++;
}
}
int main()
{
std::vector<int> vec;
vec.push_back(1);
vec.push_back(2);
for (auto l = 0; l < int(vec.size()); l++)
{
std::cout << vec[l]; //Output: "12"
}
incVector(vec);
for (auto l = 0; l < int(vec.size()); l++)
{
std::cout << vec[l]; //Output: "12"
//This output should be "23"
}
}
Obviously, what I'm actually using this for is much more complex, but this is enough of an example to get the point of what I'm trying to do across. In the actual project, it's a rabbit hole of different functions, some of which return things while others don't, so having it simply return the vector isn't an option.
I have tried making incVector accept a reference to a vector, a pointer to a vector, a pointer to a reference to a vector, and a pointer to a pointer to a vector (which are solutions that seemed to work for other similar questions) but none of those are working for me.
EDIT:
God, I feel stupid. I swear I'd tried using a reference before and it didn't work. Yet now, trying it again works just fine. Sorry! ^^;
you pass the vector by value, thus modifications are purely local:
change prototype of your function to:
void incVector(std::vector<int> &vec)
to pass by reference and get the one from main modified
Take the argument by reference. You can also use a modern for-loop.
void incVector(std::vector<int>& vec)
{
for (auto& l : vec)
{
++l;
}
}
But you don't actually need to do any of this. Applying an operation on each element of the vector can be done easily using a standard algorithm (std::for_each) and a lambda function that takes a reference to the vector's element:
#include <algorithm>
// ...
std::for_each(vec.begin(), vec.end(), [](int& l){ ++l; });
for_each is going to call the lambda for each element in the vector and pass it as the argument. Since you take the argument by reference (int&), incrementing it will increment the actual element contained in the vector.
The key point to take away from this however, is that when you want to give a new name to an object that already exists, you use a reference. Those are declared with & before their identifier:
int i = 0;
int &i_ref = i;
Here, i_ref is a reference to i, meaning it's just another name for i. Modifying i_ref is the same as modifying i.
The same applies to function arguments. If a function argument is a reference, it means it's another name for the object that was passed to the function. Modifying the reference is the same as modifying the object that was passed to the function.
Sorry about the confusing title, did not know what to title it.
I have this function:
static void smooth5(IntVector*& v)
{
IntVector* tmp = new IntVector();
for(int i=0; i<v->size(); i+=2)
tmp->push_back(v->at(i));
delete v;
v = tmp;
}
and in the main I do this:
IntVector* v = new IntVector();
v->push_back(0);
v->push_back(1);
v->push_back(2);
v->push_back(3);
smooth5(v);
//print the contents of v
When I print the contents of v, the output is 0 2.
But I did not understand what
IntVector*& v
really means when v is a pointer to an object on the heap. Can someone please explain?
IntVector*& declares a reference to a pointer. Using this as a function argument allows smooth5 to modify the caller's copy of v.
It is similar to but more readable than passing a pointer to a pointer - IntVector**.
In your example, smooth5 deletes the caller's IntVector and points it to its tmp variable instead.
This is not a pointer to reference but a reference to a pointer. This means that instead of passing a copy of the pointer to the function you pass a reference to it. So as a result any modifications you do to the pointer in the function smooth5 will affect the argument you pass to it in this case the pointer v.
What IntVector*& v in your code means depends who's reading it.
When the compiler reads it, it means that v is a reference to
a pointer to IntVector (which is a typedef to
std::vector<int>?). When I read it, it means that the author
of the code probably didn't understand how C++ works, and is
trying to adapt Java or C# idioms that aren't appropriate. The
idiomatic way of writing this would be:
void smooth5( IntVector& v )
{
IntVector results;
for ( int i = 0; i < v->size(); i += 2 ) {
results.push_back( v[i] );
}
v.swap( results );
}
Any code which involved pointers to vectors is highly
suspicious.