Why Lifetime of Temporary Doesn't Extend Till Lifetime of Enclosing Object

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.

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.

Why can I still access a reference to a temporary object?

Are you using Visual Studio by any chance? It allows your code using s1 to work as a language extension. I believe that part of the code is safe (ignoring whether it is good practice).

Only a const reference local variable can extend the lifetime of a temporary. But it's never permitted to return a local variable by reference (unless it is static). Therefore your function f is invalid.

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

Lifetime of temporary object associated with const reference (method chaining)

When you write a function thus...

const S& f(int i) const { std::cout << i << "\n"; return *this; }

...you're instructing the compiler to return a const S& and you are taking responsibility for ensuring the referenced object has a lifetime suitable for the caller's use. ("ensuring" may constitute documenting client usage that works properly with your design.)

Often - with typical separation of code into headers and implementation files - f(int) const's implementation won't even be visible to calling code, and in such cases the compiler has no insight regarding to which S a reference might be returned, nor whether that S is a temporary or not, so it has no basis on which to decide whether the lifetime needs to be extended.

As well as the obvious options (e.g. trusting clients to write safe code, returning by value or smart pointer), it's worth knowing about a more obscure option...

const S& f(int i) const & { ...; return *this; }
const S f(int i) const && { ...; return *this; }

The & and && immediately before the function bodies overload f such that the && version is used if *this is movable, otherwise the & version is used. That way, someone binding a const & to f(...) called on an expiring object will bind to a new copy of the object and have the lifetime extended per the local const reference, while when the object isn't expiring (yet) the const reference will be to the original object (which still isn't guaranteed live as long as the reference - some caution needed).

Lifetime extension of temporary by non-const reference using const-cast

Any reference can extend the lifetime of an object. However, a non-const reference cannot bind to a temporary as in your example. The Microsoft extension you refer to is not "Extend lifetime by non-const references," rather "Let non-const references bind to temporaries." They have that extension for backward compatibility with their own previous broken compiler versions.

By a cast you have forced the binding of a non-const reference to a temporary, which does not appear to be invalid, just unusual because it cannot be done directly. Once you've accomplished that binding, lifetime extension occurs for your non-const reference the same as it would for a const reference.

More information: Do *non*-const references prolong the lives of temporaries?

Extending the lifetime of a temporary value in C++

but 3 is not a real variable and it does not have a memory where it's stored. so how can this be possible ?

Actually a temporary object gets created out of the literal 3, and then that temporary is bound to the const reference. That is how it becomes possible.


Now your next question: the difference between these two

const int& ra=a;
int& const ra=a;

is that the second statement is illegal.



Related Topics



Leave a reply



Submit