4

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.

  • 2
    When you use a reference, it references the original storage address. score thus contains the address of the magic number 123, which isn't "destroyed" while your application is running. – fredrik Feb 10 '18 at 14:20
  • That makes sense. Does anyone care to advice me why the question was considered bad (negative votes) so I could write better questions in the future? I was in believe no Stackoverflow rules were broken. – Trollblender Feb 10 '18 at 14:26
  • This question is somewhat hard to answer, it's better to read http://en.cppreference.com/w/cpp/language/reference_initialization carefully, everything is explained. Question is not bad, upvoted. – llllllllll Feb 10 '18 at 14:28
  • 1
    Don't worry about it. The criteria by which people are supposed to vote here is if they consider the post "useful". As you can probably guess, that's subject to interpretation and the foul mood of whomever is voting. So take it in your stride. – StoryTeller - Unslander Monica Feb 10 '18 at 14:28
  • 1
    The lifetime of `s` will not be extended pass the function scope. The `const int& score;` is not local to a function. It's a data member. This is UB. – Ron Feb 10 '18 at 14:31
  • 2
    You have undefined behaviour https://www.ideone.com/4FXakx – Killzone Kid Feb 10 '18 at 14:33
  • What is the fix to this issue? – sattva_venu Mar 24 '22 at 13:03

2 Answers2

8

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.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
  • Is this the correct conclusion: There is automatic storage duration for the constructor argument (it will be out of scope once the constructor returns)? The reference member is set to the address of the template object and remains as a member of Person. The template object itself is saved in dynamic memory where its destiny is undefined (it may be overwritten). – Trollblender Feb 10 '18 at 15:37
  • @Trollblender - All but the last. The temporary object is just that, temporary. It's unspecified *where* it's created, only that it will expire. It could very well be allocated as local automatic objects are allocated. – StoryTeller - Unslander Monica Feb 10 '18 at 16:13
1

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.

Gabriel
  • 3,564
  • 1
  • 27
  • 49
  • All this talking about memory addresses concerns the compiler-implementation level, not the C++ language level. In particular, *"you keep the adress of where it has been allocated"* is wrong because the OP never takes the address of the referenced `int` object; there is no `&` operator anywhere in the code. – Christian Hackl Feb 10 '18 at 17:41
  • const int& score – Gabriel Feb 10 '18 at 17:48
  • That's not the `&` operator. – Christian Hackl Feb 10 '18 at 17:49
  • More precisely: the `&` character in a reference declaration has nothing to do with the address-of operator, which also uses the `&` character. Note that there's also `std::addressof`. – Christian Hackl Feb 10 '18 at 17:52
  • Correct me if I am wrong but the point of that talking is keeping track of an object being lost right? If that's the case I will replace the word adress with keep track of the object. What I wanted to point out is that you track an object with a reference, you don't hold the object. Is that clearer? – Gabriel Feb 10 '18 at 17:53
  • No, you aren't wrong, that's precisely the point. You don't hold the object but "track" it, but you don't do so by taking addresses. That's something the compiler may or may not do for you, behind the scenes, but it's generally important to distinguish clearly between references and pointers. – Christian Hackl Feb 10 '18 at 17:57