1

First question on StackOverflow so please be indulgent, and don't hesitate to give me feedback on how i could've asked the coming question better. Thanks !

I was wondering,

I know that reference to stack allocated objects take no memory space and have no overhead at exec time, i.e. :

int x;
x = 0;

and

int x;
int& y = x;
y = 0;

Should be exactly identical when compiled. This sounds obvious to me since the address of b is known at compile time (because it's the same as a, which is known at compile time)

However, i don't know how a C++ compiler handles this if the address is not known at compile time, which happens (as far as i know) when the object being referenced is allocated on the heap, i.e. :

A& x = *new A();

The value of &x in this context has to be stored somewhere in the compiled code, resulting in a different behavior than if you had a reference to an object allocated on the stack, right ?

So, i know the address of a reference to a stack allocated object has to be an r-value (because of my first example), thus one cannot modify it.

But why one cannot modify the address of a reference to a heap allocated object (since it has to be an l-value as far as i understand it, because of not being able to know it at compile time) ? Why do we only have a "reference" type, and not "reference to stack allocated object" + "reference to heap allocated objects" types, since they are obviously not treated the same way in implementation ?

Thanks for your insights !

  • From a compiler's point of view, references are just pointers with different syntax (and maybe slightly different optimization possibilities because they can't be null) – user253751 Feb 28 '20 at 11:22
  • @user253751 Hmm, OK, then why cant we modify the address of a reference if it's stored anyway ? I understand we can't if it's an r-value, but with the behavior you're telling here, it's an l-value, thus why can't we modify it ? I mean, i know we can't, so my question is what is the purpose served by this "non-modifyable" behavior ? – Matt The Unwise Feb 28 '20 at 11:53
  • 2
    You can't modify references because the standard says so. – user253751 Feb 28 '20 at 11:55
  • @user253751 and thus my question: Why does the standard says so ? ;) – Matt The Unwise Feb 28 '20 at 11:59
  • @MattTheUnwise _why cant we modify the address of a reference if it's stored anyway ?_ reference aren't stored anywhere, they're just alias, see this: https://isocpp.org/wiki/faq/references#reseating-refs – some user Feb 28 '20 at 12:02
  • @someuser in the case of a heap allocated object reference, the addresses **have to** be stored somewhere (or else there's something i'm missing, which is why i'm asking this question in the first place !). But i think i'm about to accept Some Programmer Dude's answer as soon as he confirms my last comment. – Matt The Unwise Feb 28 '20 at 12:11
  • 1
    Because it's not what references are for. If you want a reference to a heap object which contains an adress and that can be made to refer to other objects, you already have pointers. References aren't objects, they are meant to be simple aliases. If they we modifiable you would just have two ways of defining pointers. – François Andrieux Feb 28 '20 at 12:11
  • @FrançoisAndrieux Post that as an answer, i'll accept it ! – Matt The Unwise Feb 28 '20 at 12:12
  • 1
    If references needed to be compile time constant offsets (only compatible with stack objects) you couldn't use it for much. For example there would be no reference type function parameters. But that allowance incidently allows you to do things like `int & x = *new int;` even if you shouldn't. Nornally, proper usage of references should be easier to optimize out than pointera and easier to reason about – François Andrieux Feb 28 '20 at 12:16
  • Im on a phone but I will as soon as I get to a computer. – François Andrieux Feb 28 '20 at 12:16
  • 2
    @someuser In reality the compiler treats references as special pointers. That means they are stored unless the optimizer realizes the program doesn't need them. – user253751 Feb 28 '20 at 12:16
  • @user253751 And that fills the black spots there were in François's answer, thank you very much ! – Matt The Unwise Feb 28 '20 at 12:21
  • @FrançoisAndrieux could you include a link to [this](https://stackoverflow.com/questions/57483/what-are-the-differences-between-a-pointer-variable-and-a-reference-variable-in?rq=1) question in your answer, because it's almost a duplicate (my bad) . I still think what you answered has to be in the final answer because it's specific. Thanks ! – Matt The Unwise Feb 28 '20 at 12:45

3 Answers3

1

The value of &x in this context has to be stored somewhere in the compiled code, resulting in a different behavior than if you had a reference to an object allocated on the stack, right ?

How references work under the hood is up to the implementation. Each compiler is free to make them work however they want. But usually, the easiest solution is to treat T& like a T * const that can never be nullptr and whose address can never be taken. In cases where the referred object is known at compile time, then the reference can be optimized out and it's like it never existed. But this is also true of pointers. It's just that the extra property of not being addressable makes references a little bit easier to optimize out.

What you can be certain of is that the behavior will not be different based on how the referred object was created (dynamically or as a local object). Yes, the cases you show may result in the compiler doing different things, but the behavior of code is not a property of what the compiler does. This reasoning is backwards. The code in defines a strict behavior according to the language standard. Anything the compiler does has to leave that behavior intact, in so far as it respects what the standard says. So references have a behavior, and some uses of behavior will trigger different assembly to be generated but that result will behave the same.

Please note that references have a few extra requirements over pointers that makes the comparison with pointers a bit inaccurate. As an example see this question.

But why one cannot modify the address of a reference to a heap allocated object (since it has to be an l-value as far as i understand it, because of not being able to know it at compile time) ?

The reason you can't change the what a reference refers to is not a technical one. By design, you don't want to be able to change what it refers to even if it would be easy to implement that feature. This is because the immutable nature of references are considered an advantage. If you want a reference that is guaranteed to contain an address and that you can change to refer to other things, the language already provides pointers that can do this for you.

Why do we only have a "reference" type, and not "reference to stack allocated object" + "reference to heap allocated objects" types, since they are obviously not treated the same way in implementation ?

Again, it's not because the implementation treats different use cases of a feature that the interface should expose those differences to the developer. If you have are writing a void mutate(int &) function you want to handle all ints the same way, no matter how they were created or what optimizations the compiler might have come up. There isn't really a use case for a function that only works on stack allocated objects.

François Andrieux
  • 28,148
  • 6
  • 56
  • 87
0

Initializations doesn't have to be done at compile-time, the compiler can generate code to do them at run-time (if the right-hand-side isn't known or can be computed at compile-time).

This is what happens when you do

A& x = *new A;

The result of new A isn't known at compile-time, so the compiler will generate code that does the initialization of x at run-time instead.

This isn't really different from other compile-time variable initializations.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • This does not answer my question, but i know why you gave me this answer. I removed the "How does this work ?" part which was confusing. I know it doesn't matter in the end where the object lives, but what matters is if the address value is known at compile time or not, which in most case, if not all, depends on whether the object is allocated on the stack or on the heap. – Matt The Unwise Feb 28 '20 at 11:49
  • @MattTheUnwise Rewrote the answer, perhaps it better fits your question now? – Some programmer dude Feb 28 '20 at 11:56
  • So you mean: "The fact that reference's addresses cannot be modified is because a reference's behavior has to be generic (the same) whether the pointed value is know at compile time or not ?" Is that it ? Thanks for your time ! – Matt The Unwise Feb 28 '20 at 12:05
0
A& x = *new A();

How does it work ?

new operator allocates memory from the heap dynamically, after that you've assigned it's reference to x.

int x;
int& y = x;
y = 0;

Here, the reference of int x is stored to y at compile time, because the int x is already created, whereas the object x (A& x = *new A();) is initialized at runtime.

Why reference to stack allocated object treated same as reference to heap allocated object?

No it's not the case. Once you've allocated memory in the heap, it'll be there until program terminates or you manually delete it, whereas the objects/variables allocated in stack/locally get cleared whenever the stack frame of that function/scope is destroyed, so does the references.

More about changing/re assigning references: How can you reseat a reference to make it refer to a different object?

some user
  • 1,693
  • 2
  • 14
  • 31
  • That was not my question, and i don't think "the reference of int x is stored to y at compile time" is true (of course i can be wrong). In this case, i don't think the reference is ever stored, aka "y" acts as some kind of alias to "x" for the compiler. Once again, my question is not "How does `A& x = *new A();` " work, but why both types of refs, which differ in the way they are implemented, are treated as if they were identical in C++. I will update my question to clear this confusion. @some user – Matt The Unwise Feb 28 '20 at 11:43
  • it doesn't matter how the ref. is implemented, ref. are just alias... just another name of that object – some user Feb 28 '20 at 12:07