Search code examples
c++rvalue-referencexvalue

Why does referencing an xvalue not extend the lifetime of the object it refers to?


The compiler has no way of knowing whether the xvalue is actually referencing a temporary. Therefore if the xvalue is a reference to some specific persistent "non-temporary" object, we would not want tie the lifetime of this object to the new reference variable - otherwise we could actually end up destroying the persistent object prematurely.

So the real issue is that "lifetime extension" is implemented using "transfer of lifetime ownership", and this transfer is not something that we can always apply if we want want to guarantee that we don't end up destroying an object prematurely.

1 Is my description above correct?

//

It would seem a lot clearer and more useful if the lifetime extension concept was truly an "extension" internally - meaning if the object's lifetime was longer than the reference to begin with, it would not change anything, and the object would keep living even after the reference is destroyed.

2 What are the reasons that "extension" is not implemented in that way?

I can imagine that this might only be possible if additional runtime instructions are added, e.g. some type of "reference counting" as a garbage collector does, and that likely goes against c++ philosophy. Or perhaps it would be possible to implement without any additional runtime instructions, it's would just be difficult and not worth the effort.


Solution

  • Reference lifetime extension only ever applies to prvalues, and it is only applied immediately when they're created. There's no "transfer of lifetime ownership".

    When a temporary object is created, it can immediately bound to a reference. If it is, then it is created with the lifetime of that reference. The object isn't created only to later have its lifetime extended. It's lifetime is extended from the moment it starts.

    In this way, the compiler does know if an xvalue is referencing a temporary object at the time that the lifetime extension takes place.

    Consider some examples:

    const T& ref = T{};
    

    Here the temporary T object's lifetime is extended to that of ref immediately as it's created.

    T someFunction() { return {}; };
    const T& ref = someFunction();
    

    Here the return value of someFunction has its lifetime extended. Again, this happens immediately when that object is being created. Remember a function's return value is always created by the caller.


    Note that the following are not examples of reference lifetime extension:

    void someFunction(const T& ref) {}
    someFunction(T{});
    

    This is often mistaken for reference lifetime extension, but it isn't. The temporary T object passed to someFunction already has a lifetime that extends to the end of the full expression in which it's created, which is already longer than the lifetime of ref.

    const T& someFunction() { return {}; }
    const T& ref = someFunction();
    

    Here, ref does not extend the lifetime of the object returned by someFunction because there is an intermediate reference involved. ref is left dangling.