Why do const references extend the lifetime of rvalues?
Asked Answered
B

2

40

Why did the C++ committee decide that const references should extend the lifetime of temporaries?

This fact has already been discussed extensively online, including here on stackoverflow. The definitive resource explaining that this is the case is probably this GoTW:

GotW #88: A Candidate For the “Most Important const”

What was the rationale for this language feature? Is it known?

(The alternative would be that the lifetime of temporaries is not extended by any references.)


My own pet theory for the rationale is that this behavior allows objects to hide implementation details. With this rule, a member function can switch between returning a value or a const reference to an already internally existent value without any change to client code. For example, a matrix class might be able to return row vectors and column vectors. To minimize copies, either one or the other could be returned as a reference depending on the implementation (row major vs column major). Whichever one cannot be returned by reference must be returned by making a copy and returning that value (if the returned vectors are contiguous). The library writer might want leeway to change the implementation in the future (row major vs column major) and prevent clients from writing code that strongly depends on if the implementation is row major or column major. By asking clients to accept return values as const ref, the matrix class can return either const refs or values without any change to client code. Regardless, if the original rationale is known, I would like to know it.

Bordie answered 27/9, 2016 at 7:11 Comment(5)
The matrix example would be solved more elegantly using a proxy object.Foulness
You'd have to check "Design & Evolution of C++" for the definitive answer. But I strongly suspect that this question has an objective answer - personal pet theories do not matter.Fingertip
@Foulness I think what you're suggesting is the solution I was trying to pre-empt by stipulating that the returned vector must be contiguous (for whatever reason...).Bordie
@Fingertip I actually picked up a copy recently! (It's steadily making its way through my "to read" pile...)Bordie
See also this for further details on the other case around.Nicolanicolai
T
39

It was proposed in 1993. Its purpose was to eliminate the inconsistent handling of temporaries when bound to references.

Back then, there was no such thing as RVO (return value optimization), so simply banning the binding of a temporary to a reference would have been a performance hit.

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/1993/N0345.pdf

Tablespoon answered 27/9, 2016 at 7:59 Comment(5)
Wow, I was 10 years old at that time. Interesting to know the reasons indeed. +1 for the link to the paper.Nicolanicolai
Your second paragraph is not clear to me. Could you describe a case where the performance penalty of copying a temporary (possibly avoidable by RVO, but which is supposedly not done) can be circumvented by binding a temporary to a reference? Without of course creating a dangling reference.Blandishment
@MarcvanLeeuwen Without binding to a reference, it would have meant that the temporary would have to be copied to the local (non-reference) variable.Tablespoon
Yes I can see that, but what kind of temporaries could be bound to a reference without creating a dangling reference? Without the extended lifetime of temporaries rule, I think none (by definition of a temporary, its lifetime ends shortly). With the rule, the temporaries created in the statement binding the reference itself. Not within a function called from that statement, so I think RVO is irrelevant here. I can think of a temporary from a function argument taken by const ref, and the returned unchanged from the function.Blandishment
@RichardHodges "Its purpose was to eliminate the inconsistent handling of temporaries when bound to references". Imo it would be more intuitive to be destroyed just like other references (well except rvalue refs as a temporary can still bind to an rvalue ref) and thus be consistent. Still, I didn't know how these things worked back then, so I don't understand the rationale here.Arak
J
6

You are not questioning why const references are allowed to bind to temporaries, but merely why they extend the lifetime of those temporaries.

Consider this code:

struct A
{
    void foo() const;
};

A bar();

const A& a = bar();

a.foo();               // (1)

If the lifetime of the temporary returned by bar() were not extended, then any usage of a (exemplified by the line (1)) would lead to undefined behavior. This would make binding non-parameter const references to temporaries completely useless.


EDIT (addressing OP's comment):

Thus the real question should be why a const reference variable (that is not a function parameter) is allowed to bind to a temporary. I don't know the original justification for it (Richard Hodges' answer may be the only true one), but it provides us with one useful feature. Considering the following example:

struct B
{
    virtual void foo() const;
};

B bar();

const B& b = bar();

b.foo();               // (1)

The only difference of this example from the previous one is that B::foo() is virtual. Now, what if we decide to introduce a new class D as a subclass of B and change the return type of bar() from B to D?

struct B
{
    virtual void foo() const;
};

struct D : B
{
    virtual void foo() const;
};

//B bar();
D bar();

const B& b = bar();

b.foo(); // This will call D::foo()

// In the end the temporary bound by b will be correctly destroyed
// using the destructor of D.

Thus, binding const references to temporaries simplifies taking advantage of dynamic polymorphism for objects that are returned by value.

Joyjoya answered 27/9, 2016 at 7:39 Comment(4)
Correct answer, can you add something about rvalue and lvalue types of the reference expression?Hawsepipe
@Hawsepipe Sorry, but I don't see how that can improve the answer (with regard to OP's question).Joyjoya
Unless I'm confused, this answer seems a bit too hyper-literal. I don't know if the question wording was misleading but I didn't mean to suggest that it could somehow make sense for const references to bind temporaries but not extend their lifetimes. The question is intended to be about why const references can bind temporaries and extend their lifetimes.Bordie
@Leon: I think i would explain why lifetime extension is not necessary for non-const references (because they cannot bind to temporaries anyway)Hawsepipe

© 2022 - 2024 — McMap. All rights reserved.