Using a reference member out of scope - c++

This question concerns the function stack and reference members (which I read are considered bad practice in general). My test code:
#include <iostream>
using namespace std;
struct Person
{
Person(const int& s) : score(s) {}
const int& score;
};
int main()
{
Person p(123);
cout << "P's score is: " << p.score << endl;
return 0;
}
We create an integer object in Person's constructor. A template object is created because of converting int into &int (and that's why we need const). Then we set score point to the constructor's argument. Finally, we exit the constructor and the argument is destroyed.
Output:
P's score is: 123
How come we are still getting the value 123 if the argument was destroyed? It would make sense to me if we copied the argument to the member. My logic tells me the member would point to an empty location which is obviously incorrect. Maybe the argument is not really destroyed but instead it just goes out of scope?
This question arose when I read this question: Does a const reference prolong the life of a temporary?
I find Squirrelsama's answer clear and I thought I understood it until I tried this code.
Update 2/12/2018:
More information about this:
What happens when C++ reference leaves it's scope?
Update 2/18/2018:
This question was made in not clear understanding of how references, pointers and dynamic memory work in C++. Anyone struggling with this, I recommend reading about those.

How come we are still getting the value 123 if the argument was destroyed?
Because nothing guarantees you won't. In C++, accessing an object whose lifetime has ended (and your temporary is dead when you access it) results in undefined behavior. Undefined behavior doesn't mean "crash", or "get empty result". It means the language specification doesn't prescribe an outcome. You can't reason about the results of the program from a pure C++ perspective.
Now what may happen, is that your C++ implementation reserves storage for that temporary. And even though it may reuse that location after p is initialized, it doesn't mean it has to. So you end up reading the "proper value" by sheer luck.

By storing a reference in your object, the only guarantee you have is that you keep track of the object, as long as the object is valid. When the object is not valid anymore, you have access to something not valid anymore.
In your example you allocate a temporary object (123) somewhere, and you keep track of the object, via the reference mechanism. You do not have any guarantee the object you are tracking is still valid when you use this reference.

Related

Why do we need to make a local copy of smart_ptr first before passing it to other functions?

CppCon 2015: Herb Sutter "Writing Good C++14... By Default" Slide 50
Live on Coliru
I have seen the following guideline through above talk. However, I have hard time to understand the critical issues both solutions try to solve in the first place. All comments on the right side of the code are copied from original talk. Yes, I don't understand the comments on the slide either.
void f(int*);
void g(shared_ptr<int>&, int*);
shared_ptr<int> gsp = make_shared<int>();
int main()
{
// Issue 1>
f(gsp.get()); // ERROR, arg points to gsp', and gsp is modifiable by f
// Solution 1>
auto sp = gsp;
f(sp.get()); // ok. arg points to sp', and sp is not modifiable by f
// Issue 2>
g(sp, sp.get()); // ERROR, arg2 points to sp', and sp is modifiable by f
// Solution 2>
g(gsp, sp.get()); // ok, arg2 points to sp', and sp is not modifiable by f
}
Can someone please give me some advices what the problems are if we write code shown in Issue 1 and Issue 2 and why the solutions fix the problems?
f could be written like this:
void f(int* p)
{
gsp = nullptr;
*p = 5;
}
Since gsp is the only shared_ptr that owns the int, if it is cleared, then the pointer given to p is destroyed.
Basically, it is reasonable for functions which are given non-owning pointers (or references) to expect that the objects being pointed to are owned by someone outside of themselves, and that there is nothing they can do to cause them to be destroyed.
g is something similar, though with a reference as a parameter rather than a modifiable global:
void g(shared_ptr<int> &sp, int* p)
{
sp = nullptr;
*p = 5;
}
Both of these are warnings about making sure that, if some code passes an owned object to a function without explicitly transferring ownership, that function must ensure that there is nothing the called function can do to destroy that owned object by accident. In real-world scenarios, these will be fair more complex, as far as determining what has actually happened.
For cases of g, the parameter might be a reference to some complex object which happens to have the last shared_ptr to the int you also passed as a parameter. For f, the global object might be some "singleton" that manages a bunch of things, including ownership of objects, and this function just so happened to be passed a pointer to such an object. Inadvertently calling the wrong function from f or g can cause the pointer to become invalid.
Raw pointers have caused a lot of problems in C++. The key problem is that their lifetime is totally unclear. If I have a T*, then I have no idea how long that T is supposed to live, or if I'm supposed to free it, or if the caller will free it. Messing that up can result in double frees, memory leaks, and in the worst case undefined behavior.
So Herb Sutter, in his presentation, places some basic rules on the use of raw pointers. In particular, he assumes implicitly that raw pointers are always borrowed. That is, a T* never owns the underlying T and hence should never free it. (If you, for some reason, need an owned T* and can't use a smart pointer, the C++ Core Guidelines recommend using an owned<T*> type to make your intentions clear, even if the owned type itself doesn't change anything semantically)
So a T* is borrowing data. If we borrow data, we need to know that it will live at least as long as the place we're borrowing it. That's where the top of Slide 50 comes in.
In callee, assume Pointer params are valid for the call, and independent.
So, under Sutter's convention, a function that takes a T* should assume that the T* is good for at least until the function returns.
Now, if I pass the innards of a shared_ptr, it's possible that I'll invalidate it accidentally, without even knowing about it.
shared_ptr<int> gsp = make_shared<int>();
void f(int* my_pointer) {
gsp = some_other_data;
// Oh no! my_pointer is suddenly garbage!
}
int main() {
f(gsp.get());
}
At the "Oh no!" point in the code above, dereferencing my_pointer is undefined behavior. But we couldn't have possibly known that, since we never actually did anything with that pointer, just with a (theoretically unrelated) pointer.
The data a shared_ptr points to is only freed when all of the shared pointers referencing it are gone. So by creating a local variable in main that also points to the same data, we ensure that the data won't be freed until main ends, even if the function accidentally invalidates the one reference that it does have access to.
The g example is showing that the same thing can happen even without global variables. If a function takes a std::shared_ptr<T> and a T* that reference the same data, then the former could invalidate the latter unless there's another shared_ptr<T> somewhere else that the callee doesn't have access to that's anchoring it into position.

C++: Looking for correction for "returning address of local variable..." [duplicate]

This question already has answers here:
Returning a reference to a local variable in C++
(3 answers)
Closed 2 years ago.
I am, currently, taking an online C++ class to fill my time while unemployed and I have a question concerning returning the address of a local variable. I realize that this is not a recommended practice and I'm looking for an alternate solution to what I'm trying to do (return data for copying into my object "co" which is a class Coord object). BTW ... the program works as far as results goes. But it does get the warning C4172 "returning address of local variable or temporary...". Can you help me out here?
HERE ARE THE DETAILS: In my homework program I have a function that returns the address of local variable Coord co (Coord is my constructed class with two int fields ... obviously ... coordinates). My goal was to use the Coord copy constructor to copy the data using the address of that local variable.
//The calling program declares object co of class Coord as follows
Coord co;
//calling program can makes the call to function requestMove() in a couple of places.
//here is one of the calls
co = bd.requestMove(pHuman);
//The signature of requestMove() is
const Coord& requestMove(Player&);
//The called function, requestMove(), has a local Coord object nodeAddress and it populates nodeAddress. The return statement is:
return nodeAddress;
Comments and suggestions would be welcome. Thanks, much.
the object will be destroyed after the scope exits, so it's memory is unallocated, and having an object pointing to that memory means it's undefined behavior. The most optimal solution would be to change the return statement to return Coord{args...}; and change the function signature to Coord requestMove(Player&); it will take advantage of guaranteed copy ellision meaning that auto co = bd.requestMove(pHuman); will result to auto co = Coord{args...};
You can return a reference to a temporary duration object, but this is just one of the ways to shoot oneself into the foot. C++ is not a nanny language and allows this.
Consider:
foo& bar() {
foo f;
return f;
}
This code will compile, but what happens? At first you create an object f, but when you return from bar, the object f goes out of scope and is destructed (this is the essence of RAII). The returned reference will be a 'wild reference' to the object that no longer exists, and trying to access that memory location is simply undefined behaviour (which is a nice way of saying that what the program will do -- nobody knows. For all intents and purposes it can crash, it can eat your cat or boil you coffee).
The compiler simply warns you that the code won't probably do what you expect it to do.
The correction would be to return the object by value here.
foo bar() {
foo f;
return f;
}

Easiest way to modify a const variable's value

For my CS class I have a variable const int m_vin, throughout my code I am required to edit this value. I was under the impression that const variables cannot be edited. How would I go about this?
Edit:
Here are the directions for m_vin which is a member variable of an Object named Vehicle:
m_vin, a const int which represent a unique VIN–Vehicle Identification Number (no two vehicles can ever exist with the same m_vin).
In my program I am required to edit m_vin;
Word of caution: Modifying the value of such an object, no matter the technique used to do it, is cause for undefined behavior.
You could use:
int& ref = const_cast<int&>(m_vin);
ref = <new value>;
Don't blame me if your computer blows up.
It will be better to change the variable to be a non-const one.
int m_vin;
You should ask your professor/teaching assistant why is the const used there if the value of the variable is expected to be modified through out the code. That does not make sense.
You don't.
That's the whole point.
"For my CS class I have a variable const int m_vin, throughout my code I am required to edit this value." - Don't do that. You'll invoke Undefined Behaviour.
Create a copy. Edit the copy. Save the copy back to "wherever".
Don't try to modify const variables. Even though the language gives you tools to write code that will do it and that will compile, you will have code that has no well defined behaviour. Don't do that.
Constants (also called literals) are fixed values once declared and initialized. unlike variables whose value can be altered, constants - as the name implies does not change, they remain constant. Constant must have to be initialized at the time of creating it, and new values cannot be assigned later to it.
Consider using a variable if the value will ever change during the program execution.
Do not do this. Ever. As stated by R Sahu, you can try const_cast to remove const'ness. However, this is not the only guard to fight with. Sometimes (especially if this is a global-scope defined item), memory page that contains the const items could be marked as read-only by OS (I met such a thing on Win XP). So, your attempt to modify const_cast'ed item could end up with Access Violation or similar.
Again: do NOT do this.
For my CS class I have a variable const int m_vin, throughout my code I am required to edit this value. I was under the impression that const variables cannot be edited. How would I go about this?
As others have pointed out, you can't, at least not directly. And it's not good programming to even try.
Here are the directions for m_vin which is a member variable of an Object named Vehicle: m_vin, a const int which represent a unique VIN–Vehicle Identification Number (no two vehicles can ever exist with the same m_vin).
Most likely your instructor is asking you to construct different objects with differing const int m_vin. If you do that you don't need to edit them after construction since each object is initialized with a different vin.
In my program I am required to edit m_vin;
However, assuming this is sort of a programming challenge, you can change a const member variable by instantiating a new object initialized with the different value. This is best done in a friend function so you can access private variables.
One way to accomplish this is using placement new together with a constructor that has all values needed to initialize the object. First save copies of all the values in the object locally. Use std::move for dynamic values. Next, call the destructor on the object. Then use placement new with the argument list needed to reconstruct the object with the new const value.
The following code replaces m_vin with a different value.
#include <memory>
#include <string>
struct Foo {
const int m_vin;
std::string stuff;
Foo(int m_vin_, std::string stuff_) : m_vin{ m_vin_ }, stuff{ stuff_ } {}
~Foo() {}
};
int main()
{
Foo foo1(1, "SomeString");
int ci = 42 // modify ci
std::string stuff = foo1.stuff;
foo1.~Foo(); // in case there is any dynamic objects in Foo
new(&foo1) Foo(ci, stuff);
std::string x = foo1.stuff;
}
But be warned. You should also use std::launder to guarantee the const member will be correctly read. This sort of hack, while legal (I think), harms the readability of the code. People reading the code will, reasonably, assume that const member variables will not change after they are constructed. So don't do it other than to pacify an instructor with a weird request.

Binary trees in C++ using references

I wish to implement a binary tree using references instead of using pointers (which is generally what you tend to find in every book and every website on the internet). I tried the following code:
class tree_node {
private:
tree_node& left;
tree_node& right;
data_type data;
public:
void set_left(tree_node&);
// ... other functions here
};
void tree_node::set_left(tree_node& new_left) {
this.left = new_left;
}
I get the following error:
error C2582: 'operator =' function is unavailable in 'tree_node'.
I know I can easily implement it using pointers but I would like to keep my solution elegant and free of pointers. Can you tell me where I am going wrong?
You can't change the object that a reference refers to1; once you initialize a reference, it always refers to the object with which it was initialized.
You should use pointers. There is nothing wrong with using pointers for this (it's clean using pointers as well because parent nodes own their children, so cleanup and destruction is easy!)
(1) Well, you could explicitly call the object's destructor and then use placement new in the assignment operator implementation, but that's just a mess!
You cannot assign to references. What you're trying to do can't be done... without a huge amount of bending.. (you'd essentially destroy a node and create a new one each time you want to modify it.)
There's a good reason why all those other people use pointers.
References aren't just pointers with shorter syntax. They're another name for the actual object they refer to, even when used as the lhs of an assignment.
int i = 3;
int j = 4;
int &ref = i;
ref = j;
std::cout << i << "\n"; // prints 4: i itself has been modified,
// because semantically ref *is* i
That is, ref = j has the same effect as i = j, or the same effect as *ptr = j, if you had first done int *ptr = &i;. It means, "copy the contents of the object j, into whatever object ref refers to".
For the full lifetime of ref, it will always refer to i. It cannot be made to refer to any other int, that is to say it cannot be "re-seated".
The same is true of reference data members, it's just that their lifetime is different from automatic variables.
So, when you write this.left = new_left, what that means is, "copy the contents of the object new_left into whatever object this.left refers to". Which (a) isn't what you mean, since you were hoping to re-seat this.left, and (b) even if it was what you meant, it's impossible, since this.left has reference members which themselves cannot be reseated.
It's (b) that causes the compiler error you see, although (a) is why you should use pointers for this.
References in C++ don't work the same as references in other languages. Once a reference is set, at construction time, it can't be changed to anything else.
My recommendation is to use boost's shared_ptr class instead of a reference. This will free you of the concern for managing the pointer's deallocation. You may also be interested in Boost's graph library.

C++ Reference of vector

class Refvect
{
public:
vector &refv;
Refvect(int t, vector &refv = vector()) : refv(refv) { };
void operator()()
{
refv.clear();
}
};
int main ()
{
Refvect r(0);
r();
}
With Visual Studio 2010, this gives me an error : "vector iterators incompatible" at the execution, but I don't understand why (but I can insert elements in refv without any problem).
The temporary object vector() lives as long as the reference, no?
As soon as the statement
Refvect r(0);
is executed (control passes beyond the semicolon) the temporary vector<int> &refv = vector<int>() is destroyed. Now the reference stored inside the class object is dangling - not bound to any live object. Trying to access the object through such reference is undefined behavior.
The temporary object vector() lives as
long as the reference, no?
No!
Temporaries destruct at the end of the outermost enclosing expression - i.e. an expression that is embedded in a statement rather than another expression. There is no magic tracking of references to ensure that objects live as long as references to them - that would be garbage collection.
Update:
In response to your comment:
if I use const vector<int> &refv; it
works
I'm not sure how that could be. If you add const to the parameter refv then it is no longer compatible with the member and so should not compile. If you change the member to const also then you should find that you cannot call clear on it, because it that is not a const member function.
And if you've found something in C++ that "seems to work", then you're using C++ in completely the wrong way!
There a lots of ways for something to "seem to work" in C++ under highly specific special circumstances, but which will fail more obviously if those circumstances are altered. This is called "undefined behaviour" - the results may include apparent correct behaviour, sometimes.
The most common form is where data ceases to be valid even when there is still a means of accessing it, which is the situation you have here.
The correct way to use C++ is to thoroughly understand the limits of defined behaviour and stay well inside them, as far as you possibly can. In particular you need to understand how long objects live for, so you can ensure that references to objects don't live longer than the objects they refer to.