2

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?

peoro
  • 25,562
  • 20
  • 98
  • 150
  • When you pass a reference to an object constructor that takes a reference you are potentially giving the reference up for the life of the object constructed. It is then **your** responsibility to make sure the referenced object lives longer than the newly constructed object. – Martin York Dec 23 '10 at 22:48

5 Answers5

4

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.

icecrime
  • 74,451
  • 13
  • 99
  • 111
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.

doron
  • 27,972
  • 12
  • 65
  • 103
1

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.

wilhelmtell
  • 57,473
  • 20
  • 96
  • 131
  • Returning temporaries is warned by compilers (at least GCC and CLANG). Anyway shouldn't Valgrind report a runtime error? Of course it depends on plenty things, I should look at the generated code, anyway I find it difficult to think otherwise... – peoro Dec 23 '10 at 22:40
  • @peoro The code I posted results in undefined behaviour. UB means things may or may not explode, now or later, in ways you expect or ways you don't. So valgrind may or may not see something wrong in that particular run you had. – wilhelmtell Dec 23 '10 at 22:44
1

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.

Jester
  • 56,577
  • 4
  • 81
  • 125
0

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.

Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
  • 2
    `0` is a literal true, but I think in that context (const l-value reference parameter) a temporary variable is created. Your code shouldn't behave any differently from the original. – Ben Voigt Dec 23 '10 at 22:32
  • What's the difference between a literal and a temporary in this case? Anyway it works also passing `f()` to `S`'s constructor. – peoro Dec 23 '10 at 22:33
  • @Ben Voigt, I know it shouldn't behave any differently but I thought it might trigger some warning from the compiler. Now that I think about it some more I see why it might not. – Mark Ransom Dec 23 '10 at 22:49
  • @peoro: the difference is that a literal is an rvalue, whereas a temporary is an lvalue. – Steve Jessop Dec 24 '10 at 01:44