Lifetime of Temporaries

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.

The lifetime of a temporary to which several references are bound in C++

This is a defect in that section that I reported as http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1299 .

The proposed resolution is to add a term "temporary expressions". Life-time extension only happens for objects referred to by temporary expressions.

Here's my original report which I privately emailed. I think it makes clear what it's about

In the model of the Standard, there appears to be a distinction about
temporary objects, and temporary expressions.

Temporary objects are created by certain operations operations, like
functional casts to class types. Temporary objects have a limited
specified lifetime.

Temporary expressions are expressions that are so attributed because
they are used to track whether or not an expression refers to a
temporary object for the purpose of determining whether or not the
lifetime of their referent is lengthened when bound by a reference.
Temporary expressions are compile time entities.

Several paragraphs refer to "temporaries", but do not explicitly
specify whether they refer to temporary objects referred to by
arbitrary expressions, or whether they refer only to temporary
expressions. The paragraphs about RVO (paragraph 12.8p31) use
"temporary" in the sense of temporary objects (they say such things
like "temporary class object that has not been bound to a reference").
The paragraphs about lifetime lengthening (sub-clause 12.2) refer to
both kinds of temporaries. For example, in the following, "*this" is
not regarded as a temporary, even though it refers to a temporary

struct A { A() { } A &f() { return *this; } void g() { } };

// call of g() is valid: lifetime did not end prematurely
// at the return statement
int main () { A().f().g(); }

As another example, core issue 462 handles about temporary expressions
(making a comma operator expression a temporary, if the left operand
was one). This appears to be very similar to the notion of "lvalue
bitfields". Lvalues that track along that they refer to bitfields at
translation time, so that reads from them can act accordingly and that
certain reference binding scenarios can emit diagnostics.

Lifetime of temporaries

This is a special case: binding a const reference to a temporary object, stretches its lifetime until that const reference goes out of scope. This is only true for function local const references, e.g. the following will not work:

struct X
{
int const& i
X(int const& i_) : i(i_) {}
};

int f();

int main()
{
X x(f());
int u = x.i; //!
}

During construction of x, the i_ will be bound to the temporary returned by f, as will i, but although it's a const reference, that temporarie's lifetime will not be stretched to that of i, i.e. the rule does apply here.

See this GOTW article

Update: as is mentioned in the article and in the comments, the const is vital. The C++ standard allows binding of temporaries only to const lvalue references and rvalue references, so int& i = f(); is not allowed. However, MSVC has an extension that allows this, and as with other references, the lifetime of the temporary is extended until the reference goes out of scope. I would not recommend to exploit that extension, as it makes the code nonportable. In fact, I would be careful binding temporaries to references, since this feature is not well known and your colleagues might be baffled seeing it work, which means the code will lack readability.

Lifetime of temporaries

$12.2/3- "Temporary objects are
destroyed as the last step in
evaluating the full-expression (1.9)
that (lexically) contains the point
where they were created. This is true
even if that evaluation ends in
throwing an exception."

The lifetime of the temporary returned by foo() extends until the end of the full expression where it is created i.e. until the end of the function call 'bar'.

EDIT 2:

$1.9/12- "A full-expression is an
expression that is not a subexpression
of another expression. If a language
construct is defined to produce an
implicit call of a function, a use of
the language construct is considered
to be an expression for the purposes
of this definition."

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.

prolonging the lifetime of temporaries

I'll answer your question... the other way around.

Why did they allowed Foo const& foo = fooByValue(); to begin with ?

It makes life (somewhat) easier, but introduces potential undefined behavior all over the place.

Foo const& fooByReference()
{
return fooByValue(); // error: returning a reference to a temporary
}

This is obviously wrong, and indeed the compiler will dutifully report it. As per Tomalak's comment: it is not mandated by the standard, but good compilers should report it. Clang, gcc and MSVC do. I think that Comeau and icc would too.

Foo const& fooByIndirectReference()
{
Foo const& foo = fooByValue(); // OK, you're allowed to bind a temporary
return foo; // Generally accepted
}

This is wrong, but is more subtle. The problem is that the lifetime of the temporary is bound to the lifetime of foo, which goes out of scope at the end of the function. A copy of foo is passed to the caller, and this copy points into the ether.

I raised the bug on Clang, and Argyris was able to diagnose this case (kudos really :p).

Foo const& fooForwarder(Foo const&); // out of line implementation which forwards
// the argument

Foo const& fooByVeryIndirectReference()
{
return fooForwarder(fooByValue());
}

The temporary created by fooByValue is bound to the lifetime of the argument of fooForwarder, which dutifully provide a copy (of the reference), copy that is returned to the caller, even though it now points into the ether.

The issue here is that fooForwarder's implementation is perfectly fine wrt the standard, and yet it creates undefined behavior in its caller.

The daunting fact though, is that diagnosing this requires knowing about the implementation of fooForwarder, which is out of reach for the compiler.

The only solution I can fathom (apart from WPA) is a runtime solution: whenever a temporary is bounded to a reference, then you need to make sure that the returned reference does not share the same address... and then what ? assert ? raise an exception ? And since it's only a runtime solution, it is clearly not satisfactory.

The idea of binding a temporary to a reference is brittle.

What's the lifetime of temporary objects in a range-for?

The current standard says in The range-based for statement [stmt.ranged] that

The range-based for statement

for ( init-statementopt for-range-declaration : for-range-initializer ) statement

is equivalent to

{
init-statementopt
auto &&__range = for-range-initializer ;
auto __begin = begin-expr ;
auto __end = end-expr ;
for ( ; __begin != __end; ++__begin ) {
for-range-declaration = *__begin;
statement
}
}

This means that your Foo().words() is only used in the assignment auto &&__range = Foo().words(); and that the temporary object not lives until the code reaches the for loop.

Please note that I copied from the latest C++20 draft. In C++11 the code is a bit different, but the relevant part is the same.

Temporary lifetime extension

What does until the function exits mean? Does it mean untill it finished executing?

Yes.

Why do I get a 5 output. Does a temporary object still exist on line #2?

Dereferencing a reference which is not bound to a living object is undefined behavior, so you may get 5 as well as 42 as well as anything else (including a crash). You simply cannot have any expectation on a program that has undefined behavior.

How can I interpret the standard quote to figure out how this example works?

Pretty much like you did already.The temporary gets bound to the function parameter fooRef, which gets destroyed when returning from the function. Since that temporary is bound to the returned value, that object ceases to exist when the function returns. Later on, you are dereferencing a dangling reference, which gives you UB.

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.

C++ lifetime of temporaries - is this safe?

The relevant part of the standard is in §12.2:

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

Except:

12.2.4) There are two contexts in which temporaries are destroyed at a different point than the end of the full-expression. The first context is when a default construct is called to initialize an element of an array. ... [doesn't apply]

12.2.5) The second context is when a reference is bound to a 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 parameter in a function call (5.2.2) persists until the completion of the full-expression containing the call.

So there you go. The temporary stringstream{} is bound to a reference in a function call, so it persists until the completion of the expression. This is safe.



Related Topics



Leave a reply



Submit