Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

People also ask

Why can't you make a non-const reference to a literal?

A non-const reference cannot point to a literal. You cannot bind a literal to a reference to non-const (because modifying the value of a literal is not an operation that makes sense) and only l-values can be bound to references to non-const.

Can a const reference call a non-const function?

Once you have a const object, it cannot be assigned to a non-const reference or use functions that are known to be capable of changing the state of the object.

Can a constant be passed by reference?

In short, passing by reference-to-const is a potentially efficient alternative to passing by value. To the calling function, an argument passed by reference-to-const looks and acts just like one passed by value.

Can const reference be modified?

But const (int&) is a reference int& that is const , meaning that the reference itself cannot be modified.


From this Visual C++ blog article about rvalue references:

... C++ doesn't want you to accidentally modify temporaries, but directly calling a non-const member function on a modifiable rvalue is explicit, so it's allowed ...

Basically, you shouldn't try to modify temporaries for the very reason that they are temporary objects and will die any moment now. The reason you are allowed to call non-const methods is that, well, you are welcome to do some "stupid" things as long as you know what you are doing and you are explicit about it (like, using reinterpret_cast). But if you bind a temporary to a non-const reference, you can keep passing it around "forever" just to have your manipulation of the object disappear, because somewhere along the way you completely forgot this was a temporary.

If I were you, I would rethink the design of my functions. Why is g() accepting reference, does it modify the parameter? If no, make it const reference, if yes, why do you try to pass temporary to it, don't you care it's a temporary you are modifying? Why is getx() returning temporary anyway? If you share with us your real scenario and what you are trying to accomplish, you may get some good suggestions on how to do it.

Going against the language and fooling the compiler rarely solves problems - usually it creates problems.


Edit: Addressing questions in comment: 1) X& x = getx().ref(); // OK when will x die? - I don't know and I don't care, because this is exactly what I mean by "going against the language". The language says "temporaries die at the end of the statement, unless they are bound to const reference, in which case they die when the reference goes out of scope". Applying that rule, it seems x is already dead at the beginning of the next statement, since it's not bound to const reference (the compiler doesn't know what ref() returns). This is just a guess however.

2) I stated the purpose clearly: you are not allowed to modify temporaries, because it just does not make sense (ignoring C++0x rvalue references). The question "then why am I allowed to call non-const members?" is a good one, but I don't have better answer than the one I already stated above.

3) Well, if I'm right about x in X& x = getx().ref(); dying at the end of the statement, the problems are obvious.

Anyway, based on your question and comments I don't think even these extra answers will satisfy you. Here is a final attempt/summary: The C++ committee decided it doesn't make sense to modify temporaries, therefore, they disallowed binding to non-const references. May be some compiler implementation or historic issues were also involved, I don't know. Then, some specific case emerged, and it was decided that against all odds, they will still allow direct modification through calling non-const method. But that's an exception - you are generally not allowed to modify temporaries. Yes, C++ is often that weird.


In your code getx() returns a temporary object, a so-called "rvalue". You can copy rvalues into objects (aka. variables) or bind them to to const references (which will extend their life-time until the end of the reference's life). You cannot bind rvalues to non-const references.

This was a deliberate design decision in order to prevent users from accidentally modifying an object that is going to die at the end of the expression:

g(getx()); // g() would modify an object without anyone being able to observe

If you want to do this, you will have to either make a local copy or of the object first or bind it to a const reference:

X x1 = getx();
const X& x2 = getx(); // extend lifetime of temporary to lifetime of const reference

g(x1); // fine
g(x2); // can't bind a const reference to a non-const reference

Note that the next C++ standard will include rvalue references. What you know as references is therefore becoming to be called "lvalue references". You will be allowed to bind rvalues to rvalue references and you can overload functions on "rvalue-ness":

void g(X&);   // #1, takes an ordinary (lvalue) reference
void g(X&&);  // #2, takes an rvalue reference

X x; 
g(x);      // calls #1
g(getx()); // calls #2
g(X());    // calls #2, too

The idea behind rvalue references is that, since these objects are going to die anyway, you can take advantage of that knowledge and implement what's called "move semantics", a certain kind of optimization:

class X {
  X(X&& rhs)
    : pimpl( rhs.pimpl ) // steal rhs' data...
  {
    rhs.pimpl = NULL; // ...and leave it empty, but deconstructible
  }

  data* pimpl; // you would use a smart ptr, of course
};


X x(getx()); // x will steal the rvalue's data, leaving the temporary object empty

What you are showing is that operator chaining is allowed.

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

The expression is 'getx().ref();' and this is executed to completion before assignment to 'x'.

Note that getx() does not return a reference but a fully formed object into the local context. The object is temporary but it is not const, thus allowing you to call other methods to compute a value or have other side effects happen.

// It would allow things like this.
getPipeline().procInstr(1).procInstr(2).procInstr(3);

// or more commonly
std::cout << getManiplator() << 5;

Look at the end of this answer for a better example of this

You can not bind a temporary to a reference because doing so will generate a reference to an object that will be destroyed at the end of the expression thus leaving you with a dangling reference (which is untidy and the standard does not like untidy).

The value returned by ref() is a valid reference but the method does not pay any attention to the lifespan of the object it is returning (because it can not have that information within its context). You have basically just done the equivalent of:

x& = const_cast<x&>(getX());

The reason it is OK to do this with a const reference to a temporary object is that the standard extends the lifespan of the temporary to the lifespan of the reference so the temporary objects lifespan is extended beyond the end of the statement.

So the only remaining question is why does the standard not want to allow reference to temporaries to extend the life of the object beyond the end of the statement?

I believe it is because doing so would make the compiler very hard to get correct for temporary objects. It was done for const references to temporaries as this has limited usage and thus forced you to make a copy of the object to do anything useful but does provide some limited functionality.

Think of this situation:

int getI() { return 5;}
int x& = getI();

x++; // Note x is an alias to a variable. What variable are you updating.

Extending the lifespan of this temporary object is going to be very confusing.
While the following:

int const& y = getI();

Will give you code that it is intuitive to use and understand.

If you want to modify the value you should be returning the value to a variable. If you are trying to avoid the cost of copying the obejct back from the function (as it seems that the object is copy constructed back (technically it is)). Then don't bother the compiler is very good at 'Return Value Optimization'


Why is discussed in the C++ FAQ (boldfacing mine):

In C++, non-const references can bind to lvalues and const references can bind to lvalues or rvalues, but there is nothing that can bind to a non-const rvalue. That's to protect people from changing the values of temporaries that are destroyed before their new value can be used. For example:

void incr(int& a) { ++a; }
int i = 0;
incr(i);    // i becomes 1
incr(0);    // error: 0 is not an lvalue

If that incr(0) were allowed either some temporary that nobody ever saw would be incremented or - far worse - the value of 0 would become 1. The latter sounds silly, but there was actually a bug like that in early Fortran compilers that set aside a memory location to hold the value 0.


The main issue is that

g(getx()); //error

is a logical error: g is modifying the result of getx() but you don't have any chance to examine the modified object. If g didn't need to modify its parameter then it wouldn't have required an lvalue reference, it could have taken the parameter by value or by const reference.

const X& x = getx(); // OK

is valid because you sometimes need to reuse the result of an expression, and it's pretty clear that you're dealing with a temporary object.

However it is not possible to make

X& x = getx(); // error

valid without making g(getx()) valid, which is what the language designers were trying to avoid in the first place.

g(getx().ref()); //OK

is valid because methods only know about the const-ness of the this, they don't know if they are called on an lvalue or on an rvalue.

As always in C++, you have a workaround for this rule but you have to signal the compiler that you know what you're doing by being explicit:

g(const_cast<x&>(getX()));

Seems like the original question as to why this is not allowed has been answered clearly: "because it is most likely an error".

FWIW, I thought I'd show how to it could be done, even though I don't think it's a good technique.

The reason I sometimes want to pass a temporary to a method taking a non-const reference is to intentionally throw away a value returned by-reference that the calling method doesn't care about. Something like this:

// Assuming: void Person::GetNameAndAddr(std::string &name, std::string &addr);
string name;
person.GetNameAndAddr(name, string()); // don't care about addr

As explained in previous answers, that doesn't compile. But this compiles and works correctly (with my compiler):

person.GetNameAndAddr(name,
    const_cast<string &>(static_cast<const string &>(string())));

This just shows that you can use casting to lie to the compiler. Obviously, it would be much cleaner to declare and pass an unused automatic variable:

string name;
string unused;
person.GetNameAndAddr(name, unused); // don't care about addr

This technique does introduce an unneeded local variable into the method's scope. If for some reason you want to prevent it from being used later in the method, e.g., to avoid confusion or error, you can hide it in a local block:

string name;
{
    string unused;
    person.GetNameAndAddr(name, unused); // don't care about addr
}

-- Chris