Temporary objects: lifetime and Extensions

Temporary objects are often created and destroyed through the normal course of a program, without much thought. That’s not entirely true, when writing efficient code, we do try to avoid temporaries when possible. Especially when they involved non trivial constructors and destructors. What I’m referring to when I saw we don’t give much thought to temporaries, is about the lifetime of temporaries (when temporaries can’t be avoided of course). When it comes to most temporaries, they are usually limited to the expression in which they are used. However, there are cases in which the lifetime is extended, some for obvious reasons and some more obscure.

First let’s look at when a temporary object can be created [15.2 / class.temporary]:

1. When a prvalue is materialized so that it can be used as a glvalue
2. When needed by the implementation to pass or return an object of trivially-copyable type
3. When throwing an exception

In general (unless the lifetime isn’t extended), the temporary object is destroyed as the last step in the evaluation of the expression in which the temporary object was created. If there are more than one temporary created in the expression, they are destroyed in the reverse order in which they were created.

// Temporaries are created 
X x1 = X(1) + X(5);
// Temporaries no longer exist 
// after the end of the expression.


Let's look at another example:

X f( const X& i)
{
   return i;
}

const T& x = f(1); // const is necessary, 
                   // see note 1 below.

In this case we pass the value 1 (a temporary) to function f , which is then returned by value (possibly another temporary). It’s pretty clear from this example that it’s important that the temporary variable containing “1” be extended in order for the variable “i” to be able to take a reference to it. Remember, a reference really is a non-null pointer. Therefore it’s important for the temporary’s lifetime to not end before the function returns. For this to compile and be standard compliant, which it is, the temporary value containing 1 must have and extended lifetime. Let’s look a the cases of lifetime extension.

The are 3 cases when a temporary object’s lifetime is extended. The first 2 are related to arrays. The 3rd case is when a reference is bound to a temporary object, which is the case we just saw: “The temporary object to which the reference is bound or the temporary object that is the complete object of a subobject to which the reference is bound persists for the lifetime of the reference if the glvalue to which the reference is bound was obtained through one of the following” [class.temporary / 15.2.6 ]

However, the case for temporary objects passed to functions is actually an exception:

The exceptions to this lifetime rule are:

A temporary object bound to a reference parameter in a function call (8.5.1.2) persists until the completion of the full-expression containing the call.” [class.temporary / 15.2.6.9 ].

This means the extension lasts longer than the lifetime of the parameter from the function, until the end of the expression which called the function.

Taking another look at our earlier example:

X f( const X& i)
{
   return i;
}

// lifetime of the temporary containing 1 is 
// extended until the end of this expression.
const T& x = f(1);

Final thoughts:

Like I indicated at the start of this post, temporaries are often used and their lifetime assumed to be sufficient. Now you know their exact lifetimes mandated by the standard.


Note 1: If you’re curious about why keyword const is necessary on the references, have a look at this post from Herb Sutter.

Leave a Reply

Your email address will not be published. Required fields are marked *