views:

1308

answers:

10

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
+2  A: 

Why would you ever want X& x = getx();? Just use X x = getx(); and rely on RVO.

DevFred
Becouse I want to call `g(getx())` rather than `g(getx().ref())`
Alexey Malistov
@Alexey, that's not a real reason. If you do that, then you have a logical error soemwhere, because `g` is going to modify something which you cannot get your hands on anymore.
Johannes Schaub - litb
A: 

Non-const references can be written to. A temporary object is no longer valid once it goes out of scope. Writing through a reference to a no-longer-existing object can lead to crashes (the "can" being particulary nasty here as it might even work 9 out of 10 times). I think we can agree on it so far.

You could argue that the compiler could check what getx() actually returns, and determine that it would be "safe" to return a non-const reference in your case. But such a lookup would slow down compilation, and I could imagine would not be 100% safe in non-trivial cases. Additionally, the definition of getx() might be in a different translation unit, i.e. invisible to the compiler.

I assume the prohibition of non-const references to temporary objects spelled out in the standard was intended to make things easier for the compiler implementors.

DevSolar
A: 

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

In your example getX() does not return a const X so you are able to call ref() in much the same way as you could call X().ref(). You are returning a non const ref and so can call non const methods, what you can't do is assign the ref to a non const reference.

Along with SadSidos comment this makes your three points incorrect.

Patrick
+4  A: 

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.

sbk
1) "are temporary objects and will die any moment now".`X // OK` when will x die?2) "I would rethink the design of my functions" My question is not about design but about a purpose of such restriction. 3) "usually it creates problems" What problems does it create in this case with non-const reference to temporary object?
Alexey Malistov
@sbk: 1) Actually, the proper phrase is: "...at the end of the _full expression_...". A "full expression", I believe, is defined as one that's not a sub-expression of some other expression. Whether this always is the same as "the end of the statement", I'm not sure.
sbi
@sbk: 2) Actually, you _are_ allowed to modify rvalues (temporaries). It's forbidden for built-in types (`int` etc.), but it is allowed for user-defined types: `(std::string("A")+"B").append("C")`.
sbi
sbi
@sbi: 1) fair point, I didn't use the proper phrase. 2) I know, I know, you are nitpicking now. 3) not citing Stroustrop, but I'm trying to convey the same meaning. Thanks for the -1 anyway
sbk
To nitpick too: It shall be noted that "temporary" refers to more things. You can catch temporaries (exception objects are temporaries) by non-const reference just fine for example. So only *some* temporaries - those that are rvalues - cannot be bound by non-const references.
Johannes Schaub - litb
@sbk: I'm sorry if I have offended you, but I don't think 2) is nitpicking. Rvalues simply are not const unless you make them so and you can change them, unless they are built-ins. It took me a while to understand whether, with, e.g., the string example, I do something wrong (JFTR: I don't), so I tend to take this distinction serious.
sbi
I agree with sbi - this matter is *not at all* nitpicking. It's all the basis of move semantics that class type rvalues are best kept non-const.
Johannes Schaub - litb
@sbi and litb: it's nitpicking because I have addressed your points. It's right there, saying "ignoring C++0x rvalue references" , because they are not related to the question. And second, and I'm just repeating myself here, the original idea is that it doesn't make sense to modify temporary values. Why then it is allowed to call a non-const member is another question, but I believe this is the logical order of things. I'd be happy if someone can point me to a historical document that proves me wrong. (No offense taken)
sbk
I think `auto_ptr` wouldn't work if we couldn't call non-const functions on class type rvalues. `auto_ptr` is based on `operator auto_ptr_ref()` being called on non-const rvalues.
Johannes Schaub - litb
@sbk: But it does make sense to be able to modify non-const temporaries. Code like `f(g()+=h())` isn't _that_ uncommon and unthinkable - and often such code relies on the ability to call non-const member functions. I'm sorry, but I still think you're wrong on that. Non-POD rvalues can be modified, and this is an important feature that most programmers take for granted without thinking - and not only in C++1x.
sbi
@sbi: OK then, why it's not allowed to bind non-const temporaries to non-const references indeed?
sbk
sbi
@sbk, Your statement that temporaries live until the end of the statement they are contained in would mean that the following for-loop is correct: `for(char const *s = string().c_str();*s;s++) ;`. But it's wrong, because "string()" is destroyed *not* at the end of the statement, but at the end of initializing `s`, thus making the loop dereference not owned memory.
Johannes Schaub - litb
wrt `X`, i believe that's currently a defect in the Standard: it does not appear to say that only temporaries *not* bound by references yet are lifetime-extended. In fact, reading the Standard literally, i find that it says the following code is not valid if temporaries bound by references are affected by those rules: `struct A { A } int m; }; ... A().a().m` because it says that a temporary bound to the return value of a function is destroyed when the function returns. `*this` refers to a temporary, and it's bound to the return value.
Johannes Schaub - litb
+7  A: 

In your code getx() returns a temporary object, a so-called "rvalue". You can copy rvalues into "lvalues" (essentially named 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:

// this doesn't compile: 
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& x1 = getx();
g(x1); // fine
g(x2); // fine, too

Note that the next C++ standard will include rvalue references. What you know as references is therefor 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
void g(X&&); // #2, takes an rvalue reference

X x; 
g(x); // calls #1
g(getx()); // calls #2
g(X()); // calles #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:

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


X x(getx()); // x will steal the rvalue's data, leaving the temporary object empty
sbi
+2  A: 

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'

Martin York
"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?" **That is it!** You *understand* my question. But I disagree with your opinion. You say "make the compiler very hard" but it was done for const reference. You say in your sample "Note x is an alias to a variable. What variable are you updating." No problem. There is unique variable (temporary). Some temporary object (equals to 5) must be changed.
Alexey Malistov
@Martin: Dangling references aren't only untidy. They could lead to serious bugs when accessed later in the method!
rstevens
@Alexey: 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.
sbi
@rstevens: That was kind of my point ;-)
Martin York
@alexy: A reference to an invisable variable! Not that intuative.
Martin York
@alexy: The reason it is simple(r) for const references is that you can not do much with them. You can pass them to objects but for it to be usefull to the object it needs a copy. If the non cost situation was allowed you are opening a whole can of worms. People are now storeing the reference to temprary objects inside their objects (becuase they do not need a copy) which leads to a whole set of new bugs that are easy to create and hard to find (you can't tell if a reference is bound to a temp or a real variable so how do you tell when scope ends).
Martin York
A: 

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

Compiles OK but does only work correctly if RVO is in action. If it is not (which is absolutely legal!) you get a dangling reference because what you do is returning the pointer to a temporary object and store it into your reference.

This DOES NOT extend the life-time of your temporary object because the compiler does not see any relation between your reference and the temporary your created. It relies on your ref() methpod to return a reference to something that exists long enough. But since

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

takes the address of the object you just return a pointer to your temporary converted to a reference. The compiler has no chance to see the relation between the return value of getx() and the return value of ref()!

rstevens
Your answer is very close to what i wanted to know in additional qeustion. But also i am very curious about the sources of your information. If you could provide them it would be very helpful to us. Also my first question (Why is it not allowed to get non-const reference to a temporary object?) left unanswered.
Alexey Malistov
@rstevens: Are you saying the last example (`g(getx().ref())`) will or won't work?
sbi
@sbi: I think it is up to the compiler to throw away the temporary object returned by getx() after ref() has finished before g() is invoked.
rstevens
@rstevens: Then you're wrong on that. Look at the discussion under sbk's answer. Temporary objects live until the end of the full expression. In the case of `g(getx().ref())`, that's _after_ the call to `g()`. The language guarantees this and we rely on it. (This `f(ostrgstrm.str().c_str())`, for example, relies on the temporary string returned from `std::ostringstream::str()` to live until after the call of `f()`. To me, this is common code.)
sbi
@sbi: I removed that part of my answer.
rstevens
@rstevens: I removed my down-vote.
sbi
A: 

The evil workaround involves the 'mutable' keyword. Actually being evil is left as an exercise for the reader. Or see here: http://www.ddj.com/cpp/184403758

paul
A: 

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()));
Dan Berindei
A: 

Here's a very relevant post on this subject: http://cplusplus.co.il/2009/09/20/reference-to-temporary/

rmn