Does a Const Reference Class Member Prolong the Life of a Temporary

Does a const reference class member prolong the life of a temporary?

Only local const references prolong the lifespan.

The standard specifies such behavior in §8.5.3/5, [dcl.init.ref], the section on initializers of reference declarations. The reference in your example is bound to the constructor's argument n, and becomes invalid when the object n is bound to goes out of scope.

The lifetime extension is not transitive through a function argument. §12.2/5 [class.temporary]:

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 to a subobject of which the temporary is bound persists for the lifetime of the reference except as specified below. A temporary bound to a reference member in a constructor’s ctor-initializer (§12.6.2 [class.base.init]) persists until the constructor exits. A temporary bound to a reference parameter in a function call (§5.2.2 [expr.call]) persists until the completion of the full expression containing the call.

Prolonging life of a temporary object using const reference

The two links which you have referred are different in the sense that one shows the use of a local const reference and other shows the use of a class member const reference.

When we create local const references and refer to a temporary object then in this compiler extends the life of the temporary till the scope of local const reference.

Class member const reference pointing to temporary will lead to unexpected results as the life of the temporary object will not be extended beyond the constructor invoked to initialize class member reference. As explained in one of the answers the temporary will only survive till completion of the constructor.

Quoting the answer from:
Does a const reference prolong the life of a temporary?

The lifetime extension is not transitive through a function argument. §12.2/5 [class.temporary]:

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 to a subobject of which the temporary is bound persists for the lifetime of the reference except as specified below. A temporary bound to a reference member in a constructor’s ctor-initializer (§12.6.2 [class.base.init]) persists until the constructor exits. A temporary bound to a reference parameter in a function call (§5.2.2 [expr.call]) persists until the completion of the full expression containing the call.

If you analyze it correctly you will realize that in both cases the life of temporary is extended till the scope from where the references are initialized is valid. As soon as the scope from where reference goes out of scope the temporary becomes invalid.

For local const reference, scope is inside a function from where it is being initialized to a temp.
For class member const reference, scope is constructor where it is being initialized to a temp.

You should also read this GOTW article:
https://herbsutter.com/2008/01/01/gotw-88-a-candidate-for-the-most-important-const/

Does const reference prolong the life of a temporary object returned by a temporary object?

The lifetime extension only applies when a reference is directly bound to that temporary.

For example, initializing another reference from that reference does not do another extension.

However, in your code:

std::string const& foo = aBar.getTemporaryObject1().getTemporaryObject2();

You are directly binding foo to the return value of getTemporaryObject2() , assuming that is a function that returns by value. It doesn't make a difference whether this was a member function of another temporary object or whatever. So this code is OK.

The lifetime of the object returned by getTemporaryObject1() is not extended but that doesn't matter (unless getTemporaryObject2's return value contains references or pointers to that object, or something, but since it is apparently a std::string, it couldn't).

Why doesn't a const reference extend the life of a temporary object passed via a function?

It's by design. In a nutshell, only the named reference to which the temporary is bound directly will extend its lifetime.

[class.temporary]

5 There are three contexts in which temporaries are destroyed at a
different point than the end of the full-expression. [...]

6 The third 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 object bound to a reference parameter in a function call persists until the completion of the full-expression containing
    the call.
  • The lifetime of a temporary bound to the returned value in a function return statement is not extended; the temporary is destroyed
    at the end of the full-expression in the return statement.
  • [...]

You didn't bind directly to ref2, and you even pass it via a return statement. The standard explicitly says it won't extend the lifetime. In part to make certain optimizations possible. But ultimately, because keeping track of which temporary should be extended when a reference is passed in and out of functions is intractable in general.

Since compilers may optimize aggressively on the assumption that your program exhibits no undefined behavior, you see a possible manifestation of that. Accessing a value outside its lifetime is undefined, this is what return ref2; does, and since the behavior is undefined, simply returning zero is a valid behavior to exhibit. No contract is broken by the compiler.

const class can extend lifetime of const member ref of temporary?

With default arguments, the call

func();

is equivalent to

func(A()); // so func(refWrapper(A()));

So,

  • temporary A is created first (destroyed at end of full expression)
  • temporary refWrapper is created second (bound to parameter reference)
  • temporary refWrapper destroyed.
  • temporary A destroyed.

Notice that there is an exception for lifetime extension or parameter:

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

So refWrapper is destroyed at end of full expression, and not at the end of func call (which is the same moment in given example though). So destruction should be done in reverse order of construction.

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?

C++ - using const reference to prolong a member of a temporary, ok or UB?

Your code should be well-formed, because for temporaries

(emphasis mine)

Whenever a reference is bound to a temporary or to a subobject thereof, the lifetime of the temporary is extended to match the lifetime of the reference

Given A().b[4], b[4] is the subobject of b and the data member b is the subobject of the temproray A(), whose lifetime should be extended.

LIVE on clang10 with -O2

LIVE on gcc10 with -O2

BTW: This seems to be a gcc's bug which has been fixed.

From the standard, [class.temporary]/6

The third context is when a reference is bound to a temporary object.36 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:

...

[ Example:

template<typename T> using id = T;

int i = 1;
int&& a = id<int[3]>{1, 2, 3}[i]; // temporary array has same lifetime as a
const int& b = static_cast<const int&>(0); // temporary int has same lifetime as b
int&& c = cond ? id<int[3]>{1, 2, 3}[i] : static_cast<int&&>(0);
// exactly one of the two temporaries is lifetime-extended

— end example ]



Related Topics



Leave a reply



Submit