There are several questions about lifetime of constant reference on SO, but still I don't get it.
Is this piece of code valid?
struct S
{
const int &ref;
S( const int &x ) : ref(x) { }
};
int main( )
{
S s( 0 );
// ...
use( s.ref );
// ...
return 0;
}
Intuitively I'd say no, since 0
should expire after the expression (S s(0);
) is evaluated.
However both GCC and CLANG compile it fine, without warnings, and valgrind doesn't detect any runtime error.
What am I missing about references?
Seems invalid to me according to 12.2/4 :
There are two contexts in which
temporaries are destroyed at a
different point than the end of the
fullexpression. The first context is
when an expression appears as an
initializer for a declarator defining
an object. In that context, the
temporary that holds the result of the
expression shall persist until the
object’s initialization is complete.
The temporary only gets to live until s
is fully constructed, not until the point where use
is called.
As others have pointer out, the C++ standard only forces the compiler to keep the 0
temporary around for the duration for calling the constructor. In practice gcc keeps the temporary around for the duration of the main
function which results in the program running as expected. For this reason, there are no warnings or runtime errors.
But this only works accidentally. Don't rely on this behaviour.
The thing to note here is not the const
but the reference. The const is just a tool for static analysis. You need to be careful with references because they can bite.
int& f()
{
int i = 2;
return i;
}
Sometimes the compiler is smart enough to warn you about issues that would show up at run time, but sometimes it's not. Either way the compiler doesn't have to warn you about this.
Here is another tweak to your code that even valgrind complains about:
#include <iostream>
struct S
{
const int &ref;
S( const int &x ) : ref(x) { }
};
S* foo()
{
return new S(0);
}
int main( )
{
S* s = foo();
std::cout << s->ref << std::endl;
return 0;
}
This normally puts the temporary in the stack frame of the foo
function, so it gets destroyed when that function returns. This is similar to returning the address of a local variable.
The other answers have pointed out why the compiler is allowed to do this, my code is just an illustration.
0
isn't a temporary, it's a literal. Try this small change to your program:
struct S
{
const int &ref;
S( const int &x ) : ref(x) { }
};
int f()
{
return 0;
}
int main( )
{
S s( f() );
// ...
use( s.ref );
// ...
return 0;
}
I think the rule for references to a temporary only works for local variables, not members.