问题
Note: This question was originally asked as a comment by Ryan Haining on this answer.
struct A { std::string const& ref; };
// (1)
A a { "hello world" }; // temporary's lifetime is extended to that of `a`
std::cout << a.ref << std::endl; // safe
// (2)
A * ptr = new A { "hello world" }; // lifetime of temporary not extended?
std::cout << ptr->ref << std::endl; // UB: dangling reference
Question
- Why is the lifetime of the temporary extended in (1), but not in (2)?
回答1:
LONG STORY, SHORT
The compiler cannot extend the lifetime of the temporary involved innew A { "temporary " }
, because theA
created, and the temporary, has different storage durations.
A refence to what the Standard says can be found at the end of this post. The Standard explicitly says that the lifetime will not be extended, but it doesn't go into detail to why this is.
This post will try explain the reason in a way that is understandable for a broader audience, not only to the average language-lawyer.
Introduction
In C++ there are several types of different storage durations an object can have, among them are automatic- and dynamic storage duration, explained briefly below:
Automatic storage duration
The storage for an object with automatic storage duration will persist until the block in which they are created exits.
Objects declared in block-scope has automatic storage duration (unless they are declared
static
orextern
, but notregister
).Temporaries are, by definition, declared at block-scope so they too have automatic storage duration.
Dynamic storage duration
The storage for an object with dynamic storage duration will persist until it is explicitly stated that it should be released; such storage is, in other words, not bound to any specific scope.
Objects created dynamically through
operator new
have, as hinted, dynamic storage duration.The storage will be persist until a matching call to
operator delete
has been made.
Aggregate initialization with automatic storage duration
As stated in the previous section, a temporary has automated storage duration.
If we construct an aggregate with automatic storage duration, this too will have storage bound to the current scope; meaning that the lifetime of the temporary can easily be extended to match that of the aggregate.
Note: We can imagine them living in the same "box", and at the end of the scope we discard this box, which is fine; neither the temporary, nor the aggregate, will outlive the lifetime of the box.
Our implementation (A)
struct A { std::string const& ref; };
void func () {
A x { {"hello world"} };
}
Behind the scenes (A)
Since both x
, and the temporary, have automatic storage duration, the compiler can implement the function as the following, semantically equivalent, snippet:
void __func () {
std::string __unnamed_temporary { "hello world" };
A x { __unnamed_temporary };
}
Note: Both the temporary and the aggregate has their lifetime bound to the current scope, awesome!
Aggregate initialization with dynamic storage duration
Our implementation (B)
A* gunc () {
A * ptr = new A { { "hello world" } };
return ptr;
}
int main () {
A * p = gunc ();
std::cout << p->ref << std::endl; // DANGER, WILL ROBINSON!
delete p;
}
In the previous sections it has been stated that temporaries have automatic storage duration, which means that our temporary, bound to A::ref
, will be constructed on storage that resides in the current scope.
Behind the scene (B)
The semantically equivalence of gunc
can look as the below implementation:
A* gunc () {
A __unnamed_temporary { "hello world " };
A * ptr = new A { __unnamed_temporary }; // (1)
return ptr;
}
You are thinking it too, aren't you?
No longer can we extend the lifetime of our temporary to match that of the A
created with dynamic storage duration, at (1).
The problem is that automatic storage for __unnamed_temporary
will disappear as soon as we return from gunc
, effectively killing our temporary.
The dynamically created A
will however still be alive, leaving us with a dangling reference in main
.
Conclusion
The compiler is unable to extend the lifetime of any temporaries involved when creating an object through a new-initializer because the newed object, and the temporaries, will have different storage duration.
What does the Standard (n3797) say?
12.2p5
Temporary objects[class.temporary]
The temporary to which the reference is bound or the temporary that is the complete object of a subobject to which the reference is bound persists for the lifetime of the reference except:
...
A temporary bound to a reference in a new-initializer (5.3.4) persists until the completion of the full-expression containing the new-initializer.
[ Note: This may introduce a dangling reference, and implementations are encouraged to issue a warning in such case. -- end note ]
来源:https://stackoverflow.com/questions/23970565/extending-temporarys-lifetime-works-with-block-scoped-aggregate-but-not-throu