Is it safe to make a const reference member to a temporary variable?
Yes, as long as the reference is used only while the lifetime of the "temporary" variable has not ended. In the code you posted, you are holding on to a reference past the lifetime of the referenced object. (i.e. not good)
So, I assume that the compiler automatically generated a code directing the reference to another hidden variable.
No, that's not quite what's happening.
On my machine your print statement in main prints 125 instead of 0, so first let's duplicate your results:
#include <alloca.h>
#include <cstring>
#include <iostream>
struct Foo
{
double const& f;
Foo(double const& fx) : f(fx)
{
std::cout << fx << " " << this->f << std::endl;
}
double GetF() const
{
return f;
}
};
Foo make_foo()
{
return Foo(123.0 + 2.0);
}
int main()
{
Foo p = make_foo();
void * const stack = alloca(1024);
std::memset(stack, 0, 1024);
std::cout << p.GetF() << std::endl;
return 0;
}
Now it prints 0!
125.0 and 2.0 are floating point literals. Their sum is a rvalue that is materialized during the construction of the Foo object, since Foo's constructor requires a reference to a double. That temporary double exists in memory on the stack.
References are usually implemented to hold the machine address of the object they reference, which means Foo's reference member is holding a stack memory address. The object that exists at that address when Foo's constructor is called, does not exist after the constructor completes.
On my machine, that stack memory is not automatically zeroed when the lifetime of the temporary ends, so in your code the reference returns the (former) object's value. In my code, when I reuse the stack memory previously occupied by the temporary (via alloca and memset), that memory is (correctly) overwritten and future uses of the reference reflect the state of the memory at the address, which no longer has any relationship to the temporary. In both cases the memory address is valid, so no segfault is triggered.
I added make_foo and used alloca and std::memset because of some compiler-specific behavior and so I could use the intuitive name "stack", but I could have just as easily done this instead which achieves similar results:
Foo p = Foo(123.0 + 2.0);
std::vector<unsigned char> v(1024, 0);
std::cout << p.GetF() << std::endl;