views:

82

answers:

6

I'm trying to understand exact C++ (pre C++0x) behavior with regards to references and rvalues. Is the following valid?

void someFunc ( const MyType & val ) { 
    //do a lot of stuff
    doSthWithValue ( val);
} 

MyType creatorFunc ( ) { 
    return MyType ( "some initialization value" ); 
} 

int main () { 
    someFunc ( creatorFunc() ); 
    return 0; 
}

I found similar code in a library I'm trying to modify. I found out the code crashes under Visual Studio 2005.
The way I understand the above, what is happening is:
creatorFunc is returning by value, so a temporary MyType object obj1 is created. (and kept... on the stack?) someFunc is taking a reference to that temporary object, and as computation progresses the temporary object is overwritten/freed.
Now, what is mind-boggling is that the code most often works fine. What is more, with a simple piece of code I cannot reproduce the crash.
What is happening/supposed to happen here? Does is matter if the reference (val) is const or not? What is the lifespan of the object returned from creatorFunc?

+3  A: 

The lifetime of the temporary created by creatorFunc() is until the end of the someFunc call. Effectively, the next sequence point (loosely, the semicolon).

someFunc is taking a reference to that temporary object, and as computation progresses the temporary object is overwritten/freed"

It sounds like someFunc is doing "bad things". You should not be overwriting or freeing an object that is passed by const&.

Stephen
+4  A: 

The return value has the lifetime of a temporary. In C++ that means the complete expression that created the type, so the destructor of MyType shouldn't be called until after the call to someFunc returns.

I'm curious at your 'is overwritten/freed'. Certainly calling delete on this object is not OK; it lives on the stack and deleting it would probably cause heap corruption. Also, overwriting/modifying it could also be bad. Your example uses a constant "C string"; on many compilers values like this are stored in read-only memory, so attempting to modify it later could cause a crash/access violation. (I'm not sure if Visual C++ does this optimization, however).

There is a big difference between passing a temporary by const versus mutable references. Creating a mutable reference to a temporary is not allowed by standard C++, and most compilers (including GCC) will reject it, though at least some versions of Visual C++ do allow it.

If you are passing it with a mutable reference, what you want to write is:

   MyType t = creatorFunc();
   someFunc(t);
Jack Lloyd
"complete expression" would be the expression up to the first semicolon ?
Marcin K
A: 

You can only bind a temporary to a const reference.

Alexandre C.
A: 

Yes, it matters that the reference is const. You cannot bind a temporary to a non-const references, so the code would not compile otherwise.

Apart from that, your code is well-defined and will work as expected.

Alexander Gessler
A: 

I've had the same issue, and when I commented as such on SO got hammered for mentioning it before. I feel vindicated somebody else has this issue!!

I'm also still using VS 2005 (SP1), perhaps some compiler optimization error?

The result from creatorFunc() doesn't appear to remain alive for the call someFunc ( creatorFunc() ); the only thing I found to do was split this into 2 lines thus removing the temp variable.

Greg Domjan
+1  A: 

Normally a temporary object (such as one returned by a function call) has a lifetime that extends to the end of the "enclosing expression". However, a temporary bound to a reference generally has it's lifetime 'promoted' to the lifetime of the reference.

So the temporary being passed to someFunc() should remain alive and accessible until after someFunc() returns. However, if someFunc() (or whatever it might call, such as doSthWithValue()) stashes away a pointer or reference to val, for example in a collection or some other object, for use at some later time, the temporary object will no longer be alive and you will likely crash trying to use it. That might be why you seee no crash in simple code taking a reference to a temporary, while more complex code crashes.

See the following items for more details:

http://stackoverflow.com/questions/2615162/return-value-not-a-reference-from-the-function-bound-to-a-const-reference-in-t/2615199#2615199 http://stackoverflow.com/questions/2604206/c-constant-reference-lifetime/2604269#2604269

Also note that the standard allows temporaries to be bound only to const references, but as Jack Lloyd mentioned some versions of MSVC (prior to VS 2003 according to http://msdn.microsoft.com/en-us/library/cfbk5ddc.aspx) allow you to bind them to non-const references. However, in a quick test, I find that VS 2010's compiler still allows temporaries to be bound to non-const references.

Michael Burr
same thing with VS 2005, it allows to bind temporaries to non-const.
Marcin K