C++: constant reference to temporary

2019-07-15 21:44发布

问题:

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?

回答1:

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.



回答2:

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.



回答3:

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.



回答4:

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.



回答5:

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.