What is the lifetime of temporary function arguments? [duplicate]
Asked Answered
K

4

81

When creating a new instance of a MyClass as an argument to a function like so:

class MyClass
{
  MyClass(int a);
};    

myFunction(MyClass(42));

Does the standard make any guarantees on the timing of the destructor?

Specifically, can I assume that it is going to be called before the next statement after the call to myFunction() ?

Kapp answered 24/3, 2010 at 10:18 Comment(1)
Related: #2299281Orndorff
M
124

Temporary objects are destroyed at the end of the full expression they're part of.

A full expression is an expression that isn't a sub-expression of some other expression. Usually this means it ends at the ; (or ) for if, while, switch etc.) denoting the end of the statement. In your example, it's the end of the function call.

Note that you can extend the lifetime of temporaries by binding them to a const reference. Doing so extends their lifetime to the reference's lifetime:

MyClass getMyClass();

{
  const MyClass& r = getMyClass(); // full expression ends here
  ...
} // object returned by getMyClass() is destroyed here

If you don't plan to change the returned object, then this is a nice trick to save a copy constructor call (compared to MyClass obj = getMyClass();), in case return value optimization was not being applied. Unfortunately it isn't very well known. (I suppose C++11's move semantics will render it less useful, though.)

Mnemonics answered 24/3, 2010 at 10:20 Comment(8)
+1 nice answer. Tho i think you mean ) instead of } for if, while, switch etc? if(f(string())) { ... } surely won't make the string survive until } :)Zaragoza
@Johannes: No doesn't it? I thought it would.Mnemonics
@Mnemonics the full expression ends at the ). The whole thing might be a "full statement" but not a "full expression". Same in for(iterator i = begin(); i<end; i++) { ... } the compiler is not required to keep the temporary iterators resulting from i++ alive until the end of the for statement, of course. The temporaries will live only until after its full expression's end, which is the ) in this case too.Zaragoza
You said it yourself correctly: "A full expression is an expression that isn't a sub-expression of some other expression." and in the while, if, switch cases we have a full expression between the ( and ) because the statement they appear in is not an expression by any means. In your interpretation, the end of ; would not be a full-expression-end at all, because there is a larger "expression" around it if you are within a while/if or switch. I think it's easy to see that this makes no sense :)Zaragoza
@Johannes: I changed it. <sigh> After so many years of C++ you still learn something every week.Mnemonics
I didn't realize you could extend the life of a temporary object using a const reference. Good to know!Frieder
In C++11, an rvalue reference also extends the lifetime of a temporary. However, I would not worry about using either of these for performance reasons. I would expect any optimizing compiler to apply copy / move elision here, making the two versions compile to identical code (capture by value or bind to reference).Tryparsamide
Modern compilers (clang and gcc I know for sure) give you a warning if you try to std::move on a return value, saying it's stupid because of RVO, which it is.Nameless
K
29

Everyone has rightly cited 12.2/3 or similar, which answers your question:

Temporary objects are destroyed as the last step in evaluating the full-expression that (lexically) contains the point where they were created.

I find it amusing that over the next page in my printing of the standard, 12.2/4 says:

There are two contexts in which temporaries are destroyed at a different point than the end of the full-expression.

Neither of them applies to your example, they both relate to the use of temporaries in initializers. But it does go to show that you have to keep your wits about you when dealing with a tricky beast like the C++ standard.

Kowalewski answered 24/3, 2010 at 11:32 Comment(2)
It would be awesome if that second quote was intentionally aligned such that it starts on one page and ends on the next. :-)Laboy
Wow, thanks for mentioning this "exception". Temporaries in initializers was precisely the case I was bitten by.Agate
R
10

The standard does indeed offer guarantees - from section 12.2/5:

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

However, in your code it is not clear if the parameter is being passed by reference or by value, though at some point a copy constructor which does take a reference will be used.

Renwick answered 24/3, 2010 at 10:22 Comment(1)
Any sections about whether the calling function or the called function calls the destructor of the by-value parameter? Checking disassembly of VS2013 (clang or gcc unknown), the called function calls the destructor before returning. It is understandable for stdcall because the callee would pop the parameters before returning, but it surprised me for cdecl (understandable to avoid code though).Ticktack
S
3

In section 12.2, Temporary Objects, clause 3, the ANSI/ISO C standard states: "... Temporary objects are destroyed as the last step in evaluating the full-expression that (lexically) contains the point where they were created."

This is closely related to the concept of Sequence Points. When a sequence point is reached, all side-effects of expressions are guaranteed to have taken place.

Sawfish answered 24/3, 2010 at 10:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.