How come a non-const reference cannot bind to a temporary object?

Viewed 107029

Why is it not allowed to get non-const reference to a temporary object, which function getx() returns? Clearly, this is prohibited by C++ Standard but I am interested in the purpose of such restriction, not a reference to the standard.

struct X
{
    X& ref() { return *this; }
};

X getx() { return X();}

void g(X & x) {}    

int f()
{
    const X& x = getx(); // OK
    X& x = getx(); // error
    X& x = getx().ref(); // OK
    g(getx()); //error
    g(getx().ref()); //OK
    return 0;
}
  1. It is clear that the lifetime of the object cannot be the cause, because constant reference to an object is not prohibited by C++ Standard.
  2. It is clear that the temporary object is not constant in the sample above, because calls to non-constant functions are permitted. For instance, ref() could modify the temporary object.
  3. In addition, ref() allows you to fool the compiler and get a link to this temporary object and that solves our problem.

In addition:

They say "assigning a temporary object to the const reference extends the lifetime of this object" and " Nothing is said about non-const references though". My additional question. Does following assignment extend the lifetime of temporary object?

X& x = getx().ref(); // OK
11 Answers

Excellent question, and here's my attempt at a more concise answer (since a lot of useful info is in comments and hard to dig out in the noise.)

Any reference bound directly to a temporary will extend its life [12.2.5]. On the other hand, a reference initialized with another reference will not (even if it's ultimately the same temporary). That makes sense (the compiler doesn't know what that reference ultimately refers to).

But this whole idea is extremely confusing. E.g. const X &x = X(); will make the temporary last as long as the x reference, but const X &x = X().ref(); will NOT (who knows what ref() actually returned). In the latter case, the destructor for X gets called at the end of this line. (This is observable with a non-trivial destructor.)

So it seems generally confusing and dangerous (why complicate the rules about object lifetimes?), but presumably there was a need at least for const references, so the standard does set this behavior for them.

[From sbi comment]: Note that the fact that binding it to a const reference enhances a temporary's lifetimes is an exception that's been added deliberately (TTBOMK in order to allow manual optimizations). There wasn't an exception added for non-const references, because binding a temporary to a non-const reference was seen to most likely be a programmer error.

All temporaries do persist until the end of the full-expression. To make use of them, however, you need a trick like you have with ref(). That's legal. There doesn't seem to be a good reason for the extra hoop to jump through, except to remind the programmer that something unusual is going on (namely, a reference parameter whose modifications will be quickly lost).

[Another sbi comment] The reason Stroustrup gives (in D&E) for disallowing the binding of rvalues to non-const references is that, if Alexey's g() would modify the object (which you'd expect from a function taking a non-const reference), it would modify an object that's going to die, so nobody could get at the modified value anyway. He says that this, most likely, is an error.

Related