views:

357

answers:

3

Why does this:

#include <string>
#include <iostream>
using namespace std;

class Sandbox
{
public:
    Sandbox(const string& n) : member(n) {}
    const string& member;
};

int main()
{
    Sandbox sandbox(string("four"));
    cout << "The answer is: " << sandbox.member << endl;
    return 0;
}

Give output of:

The answer is:

Instead of:

The answer is: four

+9  A: 

Only local const references prolong the lifespan.

The standard specifies such behavior in §8.3.5/5, [dcl.init.ref], the section on initializers of reference declarations. The reference in your example is bound to the constructor's argument n, and becomes invalid when the object n is bound to goes out of scope.

The lifetime extension is not transitive through a function argument. §12.2/5:

The second context is when a reference is bound to a temporary. The temporary to which the reference is bound or the temporary that is the complete object to a subobject of which the temporary is bound persists for the lifetime of the reference except as specified below. A temporary bound to a reference member in a constructor’s ctor-initializer (12.6.2) persists until the constructor exits. A temporary bound to a reference parameter in a function call (5.2.2) persists until the completion of the full expression containing the call.

Potatoswatter
You should also see GotW #88 for a more human-friendly explanation: http://herbsutter.com/2008/01/01/gotw-88-a-candidate-for-the-most-important-const/
Nathan Ernst
+2  A: 

Because your temporary string went out of scope once the Sandbox constructor returned, and the stack occupied by it was reclaimed for some other purposes.

Generally, you should never retain references long-term. References are good for arguments or local variables, never class members.

Fyodor Soikin
"Never" is an awfully strong word.
Fred Larson
*never class members* unless you need to keep a reference to an object. There are cases where you need to keep references to other objects, and not copies, for those cases references are a clearer solution than pointers.
David Rodríguez - dribeas
+1  A: 

Here's the simplest way to explain what happened:

In main() you created a string and passed it into the constructor. This string instance only existed within the constructor. Inside the constructor, you assigned member to point directly to this instance. When when scope left the constructor, the string instance was destroyed, and member then pointed to a string object that no longer existed. Having Sandbox.member point to a reference outside its scope will not hold those external instances in scope.

If you want to fix your program to display the behavior you desire, make the following changes:

int main()
{
    string temp = string("four");    
    Sandbox sandbox(temp);
    cout << sandbox.member << endl;
    return 0;
}

Now temp will pass out of scope at the end of main() instead of at the end of the constructor. However, this is bad practice. Your member variable should never be a reference to a variable that exists outside of the instance. In practice, you never know when that variable will go out of scope.

What I recommend is to define Sandbox.member as a const string member; This will copy the temporary parameter's data into the member variable instead of assigning the member variable as the temporary parameter itself.

Legatou