0

I am interested in how the following code:

int&& c = 2;
c++;
std::cout << c; //3

keeps the variable 'c' in memory?

How the compiler implements the reference at the machine level? Does it set aside any memory for it? If so, where? Or it keeps it in CPU register?

Georgi Popov
  • 43
  • 2
  • 9
  • 1
    As reference don't exist ( _"...The following entities are not objects: value, reference..."_ [Object](https://en.cppreference.com/w/cpp/language/object) ) the compiler can optimize them away. In this case it works out at compile time that you just want to output `3` ( `mov esi, 3` ) - live - https://godbolt.org/z/M5nKeG6MW – Richard Critten Jun 03 '22 at 09:38
  • If you are interested in what implementation does, then the best way is to inspect the generated (disassembled) machine code. – Daniel Langr Jun 03 '22 at 10:19
  • I closed the question as duplicate under the impression that you are asking about the lifetime behavior on the language level. If you really wanted to know how the compiler implements the reference at the machine level, then please clarify that (and preferably specify which compiler/platform you are interested in) and I'll reopen the question. – user17732522 Jun 03 '22 at 10:29
  • It turns out that in order to demystify many of the questions I have, I would have to learn how to read assembly code – Georgi Popov Jun 04 '22 at 08:25
  • @RichardCritten Not being an object is not the same thing as not existing. When they can't be optimized away, they're implemented as pointers. – HolyBlackCat Jun 04 '22 at 08:27
  • 1
    Unless optimized away, the reference is on the stack, and so is the temporary that has its lifetime prolonged. – HolyBlackCat Jun 04 '22 at 08:28
  • @RichardCritten I know it's not in the standard, but that's how they work in practice. *"valid when trying to get the basic concept of an alias across"* Maybe, but what I'm saying is that the "magical alias" analogy breaks down in some cases. Add a reference to a class and you'll see its size being increased by the size of a pointer; examine the memory layout of it and you'll see a pointer, etc. – HolyBlackCat Jun 04 '22 at 08:46
  • @HolyBlackCat have removed my comment as I felt I was being argumentative; sorry for that and thanks for your considered response. – Richard Critten Jun 04 '22 at 08:50

1 Answers1

1

Whether or not a reference is bound to a lifetime-extended temporary is a property that is determined at compile-time for the specific reference.

For example in

{
    int x = 2;
    int&& c = std::move(x);
    c++;
    std::cout << c; //3
}

the value category of std::move(x) is xvalue and therefore it doesn't result in the creation of a temporary whose lifetime would be extended.

On the other hand in

{
    int&& c = 2;
    c++;
    std::cout << c; //3
}

the initializer expression 2 is a prvalue. A prvalue needs to be materialized into a temporary to which the reference can bind. Because the materialization happens directly before binding the reference, the lifetime of the temporary is extended to match that of the reference.

Effectively, the two snippets do the same thing and the compiler can simply transform the second one into the first one. Consequently the usual rules for storage of objects apply. The int object (either the variable or the temporary) will typically be stored on the stack or in a register depending on whether the compiler will end up needing it to have an address. The reference is typically implemented similar to a pointer, also on the stack or in a register.

However, in such a simple example, the compiler doesn't need to store the reference at all, since it knows where the variable or temporary which the reference references is stored in the function. In machine instructions all uses of the reference can simply be replaced with memory or register references to the object.

Practically speaking the snippet will then be equivalent to

{
    int c = 2;
    c++;
    std::cout << c; //3
}

Furthermore, usual optimizations apply and the compiler will in the case in your question simply do constant-propagation and see that you are just outputting the same number all the time. Instead of storing either the variable or the reference, it can just optimize the whole thing to std::cout << 3;.

user17732522
  • 53,019
  • 2
  • 56
  • 105