Guaranteed Lifetime of Temporary in C++

Guaranteed lifetime of temporary in C++?

The destructor for that sort of temporaries is called at the end of the full-expression. That's the most outer expression which is not part of any other expression. That is in your case after the function returns and the value is evaluated. So, it will work all nice.

It's in fact what makes expression templates work: They can keep hold references to that sort of temporaries in an expression like

e = a + b * c / d

Because every temporary will last until the expression

x = y

Is evaluated completely. It's quite concisely described in 12.2 Temporary objects in the Standard.

What will be the lifetime of temporary instance?

The temporary is destroyed at the end of the full-expression. That means after foo has returned.

A full-expression is an expression that is not a subexpression of another expression.

Lifetime of temporary within operator+ sequence

Yes, in the usual case: "Temporary objects are destroyed as the last step in evaluating
the full-expression (1.9) that (lexically) contains the point where they were created." (§12.2/3).

There a couple of exceptions to this, but they don't apply here.

Does standard C++11 guarantee that temporary object passed to a function will have been destroyed after the end of the function?

Well, you already quoted all the text that tells us the temporary's lifetime ends at the end of the full-expression. So, yes, "T destroyed" will always come last.

If the destruction had no observable side-effects then, per the as-if rule, it could actually happen at any time afterwards… but that's moot because, well, it wouldn't be observable.

However, the final two snippets you presented are not generally equivalent, because you fixed the order of construction/initialisation in a way that it wasn't before. Function arguments have an unspecified evaluation order. Again, though, for this particular T the difference is not observable.

C++: Life span of temporary arguments?

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.)

Why lifetime of temporary doesn't extend till lifetime of enclosing object?

The standard considers two circumstances under which the lifetime of a temporary is extended:

§12.2/4 There are two contexts in which temporaries are destroyed at a different point than the end of the fullexpression. The first context is when an expression appears as an initializer for a declarator defining an object. In that context, the temporary that holds the result of the expression shall persist until the object’s initialization is complete. [...]

§12.2/5 The second context is when a reference is bound to a temporary. [...]

None of those two allow you to extend the lifetime of the temporary by a later binding of the reference to another const reference. But ignore the standarese and think of what is going on:

Temporaries are created in the stack. Well, technically, the calling convention might mean that a returned value (temporary) that fits in the registers might not even be created in the stack, but bear with me. When you bind a constant reference to a temporary the compiler semantically creates a hidden named variable (that is why the copy constructor needs to be accessible, even if it is not called) and binds the reference to that variable. Whether the copy is actually made or elided is a detail: what we have is an unnamed local variable and a reference to it.

If the standard allowed your use case, then it would mean that the lifetime of the temporary would have to be extended all the way until the last reference to that variable. Now consider this simple extension of your example:

B* f() {
B * bp = new B(A());
return b;
}
void test() {
B* p = f();
delete p;
}

Now the problem is that the temporary (lets call it _T) is bound in f(), it behaves like a local variable there. The reference is bound inside *bp. Now that object's lifetime extends beyond the function that created the temporary, but because _T was not dynamically allocated that is impossible.

You can try and reason the effort that would be required to extend the lifetime of the temporary in this example, and the answer is that it cannot be done without some form of GC.

Extending the lifetime of a temporary object without copying it

Why is the lifetime of object x not extended past the function call even if a const reference has been bound to it?

Technically, the lifetime of the object is extended past the function call. It is not however extended past the initialization of wrap. But that's a technicality.

Before we dive in, I'm going to impose a simplification: let's get rid of wrapper. Also, I'm removing the template part because it too is irrelevant:

const object &function(const object &arg)
{
return arg;
}

This changes precisely nothing about the validity of your code.

Given this statement:

const object &obj = function(object{}); // Let's call that temporary object x

What you want is for the compiler to recognize that "object x" and obj refer to the same object, and therefore the temporary's lifetime should be extended.

That's not possible. The compiler isn't guaranteed to have enough information to know that. Why? Because the compiler may only know this:

const object &function(const object &arg);

See, it's the definition of function that associates arg with the return value. If the compiler doesn't have the definition of function, then it cannot know that the object being passed in is the reference being returned. Without that knowledge, it cannot know to extend x's lifetime.

Now, you might say that if function's definition is provided, then the compiler can know. Well, there are complicated chains of logic that might prevent the compiler from knowing at compile time. You might do this:

const object *minimum(const object &lhs, const object &rhs)
{
return lhs < rhs ? lhs : rhs;
}

Well, that returns a reference to one of them, but which one will only be determined based on the runtime values of the object. Whose lifetime should be extended by the caller?

We also don't want the behavior of code to change based on whether the compiler only has a declaration or has a full definition. Either it's always OK to compile the code if it only has a declaration, or it's never OK to compile the code only with a declaration (as in the case of inline, constexpr, or template functions). A declaration may affect performance, but never behavior. And that's good.

Since the compiler may not have the information needed to recognize that a parameter const& lives beyond the lifetime of a function, and even if it has that information it may not be something that can be statically determined, the C++ standard does not permit an implementation to even try to solve the problem. Thus, every C++ user has to recognize that calling functions on temporaries if it returns a reference can cause problems. Even if the reference is hidden inside some other object.

What you want cannot be done. This is one of the reasons why you should not make an object non-moveable at all unless it is essential to its behavior or performance.



Related Topics



Leave a reply



Submit