Returning Temporary Object and Binding to Const Reference

return const reference vs temporary object

Even if you assign it to a const reference, the return value is declared as passed by value, that means it'll be copied[1] to outside as a temporary object, and then binded to the const reference. Binding temporary object to a const reference is fine, the object won't be destroyed until getting out of the lifetime of the const reference.

On the other hand, returning reference of a local variable is illegel. The local variable'll be destroyed when the function returned, that means the outside reference will be dangled.

EDIT

My point is that if assigning a const ref to a returned local variable is legal, then shouldn't assigning a const ref to a returned const ref of a local variable be legal as well?

The point is the 1st case is not assigning a const ref to a returned local variable, it's assigning a const ref to a returned temporary variable. (Which might copied from the local variable.)


[1] The copy might be omitted according to RVO technically.

Returning temporary object and binding to const reference

This is a C++ feature. The code is valid and does exactly what it appears to do.

Normally, a temporary object lasts only until the end of the full expression in which it appears. However, C++ deliberately specifies that binding a temporary object to a reference to const on the stack lengthens the lifetime of the temporary to the lifetime of the reference itself, and thus avoids what would otherwise be a common dangling-reference error. In the example above, the temporary returned by foo() lives until the closing curly brace.

P.S: This only applies to stack-based references. It doesn’t work for references that are members of objects.

Full text: GotW #88: A Candidate For the “Most Important const” by Herb Sutter.

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

const reference to member of temporary object

Yes, this code is perfectly acceptable. The rules, according to the standard are ([class.temporary]):


  1. There are two contexts in which temporaries are destroyed at a
    different point than the end of the fullexpression. The first context
    is when a default constructor is called to initialize an element of an
    array. If the constructor has one or more default arguments, the
    destruction of every temporary created in a default argument is
    sequenced before the construction of the next array element, if any.

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

As you can see the highlighted line makes it clear that binding reference to sub-objects is acceptable, as the complete object has to have its lifetime extended as well.

Note that first does qualify as a subobject [intro.object]:


  1. Objects can contain other objects, called subobjects. A subobject can
    be a member subobject (9.2), a base class subobject (Clause 10), or an
    array element. An object that is not a subobject of any other object
    is called a complete object.

about returning a const reference to a temporary argument

The behaviour is undefined.

Binding a const reference to an anonymous temporary extends the lifetime of that anonymous temporary to the lifetime of that const reference.

But the attempted re-binding of the returned reference to a in foo will not extend the lifetime: lifetime extension is not transitive. So a is a dangling reference in main().

why can temporary objects be bound to const reference?


If the object is temporary, why would making the reference const have any effect on the lifetime of the temporary object?

In the present context, the issue is not the lifetime of the object but whether you can modify it.

Say you make a call.

foo(10);

The object that holds the value 10 in the call should not be modified by the function. If the interface of foo is:

void foo(int& ref);

it's fair to implement foo as:

void foo(int& ref)
{
ref = 20;
}

That would be a problem given the call foo(10). It won't be a problem if foo uses a const&.

void foo(int const& ref)
{
ref = 20; // Not allowed.
}

From C++11 Standard, Temporary Objects/1

Temporaries of class type are created in various contexts: binding a reference to a prvalue ([dcl.init.ref]), returning a prvalue ([stmt.return]), a conversion that creates a prvalue, ...

and from C++11 Standard, References/5.2:

-- Otherwise, the reference shall be an lvalue reference to a non-volatile const type (i.e., cv1 shall be const), or the reference shall be an rvalue reference.

A temporary can only bind to a reference to a prvalue. The type of such a reference must be a const qualified lvalue reference or a rvalue references.

MS Visual Studio compilers have allowed binding of non-const references to temporary objects but it is not sanctioned by the standard.

About binding a const reference to a sub-object of a temporary

This is covered by CWG 1651:

The resolution of issues 616 and 1213, making the result of a member
access or subscript expression applied to a prvalue an xvalue, means
that binding a reference to such a subobject of a temporary does not
extend the temporary's lifetime. 12.2 [class.temporary] should be
revised to ensure that it does.

The status quo is that only prvalues are treated as referring to temporaries - thus [class.temporary]/5 ("The second context is when a reference is bound to a temporary.") is not considered applicable. Clang and GCC have not actually implemented issue 616's resolution, though. center().x is treated as a prvalue by both. My best guess:

  • GCC simply didn't react to any DRs yet, at all. It doesn't extend lifetime when using scalar subobjects, because those are not covered by [dcl.init.ref]/(5.2.1.1). So the complete temporary object doesn't need to live on (see aschelper's answer), and it doesn't, because the reference doesn't bind directly. If the subobject is of class or array type, the reference binds directly, and GCC extends the temporary's lifetime. This has been noted in DR 60297.

  • Clang recognizes member access and implemented the "new" lifetime extension rules already - it even handles casts. Technically speaking, this is not consistent with the way it handles value categories. However, it is more sensible and will be the correct behavior once the aforementioned DR is resolved.

I'd therefore say that GCC is correct by current wording, but current wording is defective and vague, and Clang already implemented the pending resolution to DR 1651, which is N3918. This paper covers the example very clearly:

If E1 is a temporary expression and E2 does not designate a
bit-field, then E1.E2 is a temporary expression.

center() is a temporary expression as per the paper's wording for [expr.call]/11. Thus its modified wording in the aforementioned [class.temporary] /5 applies:

The second context is when a reference does not bind directly (8.5.3
dcl.init.ref) or is initialized with a temporary expression (clause 5). The corresponding temporary
object (if any) persists for the lifetime of the reference
except: [...inapplicable exceptions...]

Voilà, we have lifetime extension. Note that "the corresponding temporary object" is not clear enough, one of the reasons for the proposal's deferment; it will assuredly be adopted once it gets revised.


is an xvalue (but not a bit-field), class prvalue, array prvalue or function lvalue and “cv1 T1” is reference-compatible with “cv2 T2”, or […]

Indeed, GCC respects this fully and will extend lifetime if the subobject has array type.



Related Topics



Leave a reply



Submit