1

I've read a lot about how the standard does not allow temporaries to be passed by non-const reference, but I could find anything convincing on why is that.

The usual argument I encounter is that it is unsafe because the lifetime of the value is unknown. But in reality it is not, it is bound to the function, whose parameter it is, and will remain "alive" until that function returns, so it is safe to use it inside that function, pass it to another function inside it and another... Basically as long as it is all synchronous execution, it should be safe, since the object will remain there until the first function receiving it returns.

What is the fundamental difference between the two approaches?

doSomething(createSomething());

{
    something s = createSomething();
    doSomething(s);
}

aside from the second one unnecessarily polluting the scope with an identifier for an object you will only use once.

The way I see it, the fact that it is a temporary only limits the potential to do damage, since it will no longer be used after that function call.

Can someone provide a snipped with what kind of bad things may happen in practice by passing a temporary by reference?

Also, my question is strictly pre-c++11, so rvalue references are outside its scope.

EDIT: From the linked question, sbi's answer:

// this doesn't compile: 
g(getx()); // g() would modify an object without anyone being able to observe

But that implies that the only reason one would ever pass something by reference is to be able to observe changes made in that function after it has returned. Now, OBVIOSLY, if you use a temporary it goes without saying this is not part of your intent, nor is the reason just mentioned the only reason one would pass by reference. Which is why the scope of that question is not the same as the one labeled duplicate. You are just as likely to pass by reference to avoid a costly copy and your design can involve use of that object which is entirely encapsulated within that function and functions called within.

That other question focuses on one use of pass by reference and doesn't explain what could go wrong, nor does it take into consideration the very fact that using a temporary negates that one concern the answers address. It basically answers "because it would modify an object without anyone being able to observe", which is rightfully presented as pointless, but which is CLEARLY not the intent when you use a temporary in the first place.

To put it in other words, the answer says "You can't do it, because you can't observe the changes when you don't want to observe the changes"... DO'H How can the limitation be the inability to do something you clearly don't want to do?

  • before digging into the reasons, it's worth mentioning that in c++11 and onward you can pass an r-value reference to a non-const temporary. – Richard Hodges Dec 13 '14 at 22:52
  • @remyabel - I've seen that, it doesn't answer my question. –  Dec 13 '14 at 22:57
  • As (briefly) described in an answer to the linked question, non-const reference parameters are either *out* or *in-out* parameters. Which means that you *lose* information when passing in a temporary. Additionally, temporaries can be created by the compiler. Stroustrup uses examples (D&E, TC++PL) like `void foo(int&); double x = 41; foo(x);`, where a temporary would be created and bound to the reference parameter. The variable `x` is not modified, which is surprising. – dyp Dec 13 '14 at 23:05
  • @user3735658 it's the same question; if the existing answers are insufficient then any new answers should go on the existing question – M.M Dec 13 '14 at 23:07
  • @dyp - well, I wouldn't say surprising, just implicit conversions which some people are not always aware of. –  Dec 13 '14 at 23:07
  • @MattMcNabb If the OP is not satisfied with the existing answers, IMHO it's fine to post a new answerquestion, provided that the OP points out exactly *what* isn't convincing / what is missing from the existing answers. Maybe this could also be a comment to a specific answer. – dyp Dec 13 '14 at 23:08
  • This Q could be reopened if you take out the preamble and make it about what the difference is between your two code snippets – M.M Dec 13 '14 at 23:09
  • @MattMcNabb Sorry, typo. – dyp Dec 13 '14 at 23:11
  • @MattMcNabb - from that other queston's accepted answer: "Basically, you shouldn't try to modify temporaries for the very reason that they are temporary objects and will die any moment now" - ANY MOMENT NOW? Really? That phrase alone should be clear at explaining why I did not find it satisfactory since that is not the case, as the lifetime of temporaries is just as certain as that of any other object. –  Dec 13 '14 at 23:12
  • @user3735658 imo, the answer posted by "sbi" gives is the primary reason that this is disallowed. – M.M Dec 13 '14 at 23:14
  • @user3735658 With regards to a programming language, anything that is counter-intuitive for one individual is *surprising* for that individual. If an individual knew all language rules by heart, there wouldn't be *any* surprises. From my point of view, if I call `foo(x)` and `foo` has an out-parameter, I expect `x` to be modified. – dyp Dec 13 '14 at 23:14
  • By the way, Stroustrup seems to me to imply in D&E that it needs a reason to allow rvalue to bind to *any* reference. From D&E: "The reason to allow references to be initialized by non-lvalues was to allow the distinction between call-by-value and call-by-reference to be a detail specified by the called function [...] It is important that `const` references can be initialized by non-lvalues and lvalues of types that require conversion. In particular, this is what allows a Fortran function to be called with a constant." – dyp Dec 13 '14 at 23:19
  • @MattMcNabb - read the edit I made. Do you see the logical paradox? –  Dec 13 '14 at 23:30
  • There's no paradox. You say "obviously" and "clearly" but it's not obvious nor clear to anyone else; if a function accepts a parameter by non-const reference then it means the function may modify the value passed (i.e. an "in-out" or "out" parameter). For "in" parameters , use a const reference. – M.M Dec 13 '14 at 23:47
  • If you want to circumvent the limitation, you could use `template T& forward_mutable_temporary(T const& tmp) { return const_cast(tmp); }`, then `doSomething( forward_mutable_temporary(createSomething()) );`. – dyp Dec 13 '14 at 23:55
  • @MattMcNabb - but then you cannot edit it. And what is not obvious, I cannot help but wonder? If you use a temporary you cannot possibly intend it to be an "out" parameter. How non-obvious is that? You really cannot envision a situation you may want it to be non-const, and only "in" and by reference for the sake of avoiding a copy? For a reason other than the standard not envisioning it? –  Dec 13 '14 at 23:55
  • @dyp - yeah, how didn't I think of it. `forward_mutable_temporary()` is so much better than `something s` and so much more elegant, and just to make sure I don't get misunderstood, I am being sarcastic :) The point of using a temporary is to save some typing and make it lean and clean, which that template entirely defeats the purpose of. –  Dec 13 '14 at 23:58
  • It is meant in the same sense as `std::move`: You could live without it, but that is considered dangerous by some. Some people have argued that binding temporaries to lvalue-references is dangerous, so it cannot be done implicitly. If you want to do it anyway, you have to explicitly say so, which implies making readers of your code aware of it. – dyp Dec 14 '14 at 00:13
  • @dyp - do you realize that all arguments presented here and referred to didn't even come close to answering the question why is it not allowed and how is it presumably bad? –  Dec 14 '14 at 00:20
  • I no do not realize that. In fact, I think the first paragraph of the answer by Richard Hodges below states the *actual* reason: this feature was introduced in CFront 2.0, where Stroustrup still had full control (AFAIK). Stroustrup wanted to protect people from accidentally modifying a compiler-generated prvalue, and that's how he solved it. If you want to discuss whether or not this was a good solution, I think StackOverflow is not the appropriate place (SO is not a discussion forum). – dyp Dec 14 '14 at 00:30
  • @user3735658 The language designers asked the question "What might be the likely reason to pass in an object by modifiable reference?" and arrived at the answer that the **most likely** intention would be to use the (potentially modified) object after the function returns to the scope of the caller. With this assumption of most likely usage, passing in a temporary by modifiable reference becomes a possible programming error, and is hence disallowed. I do not know the reasons behind why you might want to pass in a temporary by modifiable reference. Care to elaborate? – Srikanth Dec 14 '14 at 00:36
  • @Srikanth - because a copy might not be very efficient or even applicable for objects whose identity matters and cannot be copied. I repeated that a few times already. –  Dec 14 '14 at 00:41
  • @user3735658 If your object is indeed non-copyable, how does `getx()` return that object? Your object must either be copyable or movable to be returnable. In either case, it's one extra line of code to pass a modifiable reference to `doSomething()`. Sure, it's an extra line of code and it "pollutes" your scope. But do you accept that this use case is in the minority? If you disagree, then you have a fundamental disagreement with the language designers - which is fine, but at least you get an answer to your question. – Srikanth Dec 14 '14 at 00:51
  • @Srikanth I don't mean non-copyable as in it has disabled copy constructor but as in "a copy would do you no good". The question is why is it not allowed, considering that in the case of "in-out" it is a non-issue, since the use of temporary rules that out, and in the case of "in" only, there is no issue, in the first case you would pass by reference it is a non-issue, in the second there is no issue, so why is it not allowed? Are you following? The question is not what the textbook says, but why does it say it. –  Dec 14 '14 at 01:05
  • @user3735658 By "textbook" do you mean the standard? If so, you're looking at this the wrong way. The design of the language need not support all use cases. As I've already explained, your use case is simply not common enough to warrant supporting this and risking the non-detection of errors in the common case. Simply put, the number of programmers being saved by the compiler error ("_d'oh, yeah, I totally didn't mean to pass a modifiable temporary..."_) is much greater than programmers that have your use case. – Srikanth Dec 14 '14 at 01:49
  • @user3735658 that's the point, you should not be editing an "in" parameter. – M.M Dec 14 '14 at 02:49

1 Answers1

0

The reason is that Mr Stroustrup reasoned that passing a reference to a mutable temporary would in all likelihood be the result of a programming error - since the result of any computation in the temporary cannot be accessed after the function call.

example:

struct foo {...};

func(foo());
// the foo has already been destroyed here, so any modified state is inaccessible.

In fairness, despite this looking short-sighted since the development of r-value references his reasoning was sound. Although it might require one more line of code, there is always a way to achieve the same effect.

examples:

{
  foo f;
  func(f);
}

or

foo_func();

where:

void foo_func() {
  foo f;
  func(f);
}

and here's a requote of the example that I have seen cited as the reason you can't bind a mutable temporary to an l-value in a function call. The bug is subtle, but serious and very difficult to find:

void inc(int& arg) { arg++; }

typedef long MyInt;

int main() {
  MyInt i = 0;
  inc(i);  // <<-- HINT: implicit conversion from long to int creates a temporary COPY, not reference
  if (i == 1) {
     // Life-saving logic
  }
}

source: comp.lang.c++.moderated

Richard Hodges
  • 68,278
  • 7
  • 90
  • 142
  • But you can access and use it potentially millions of time within that function call. It might easily be all you need :) –  Dec 13 '14 at 22:58
  • I didn't say he was correct :-) – Richard Hodges Dec 13 '14 at 22:59
  • So Mr Stroustrup assumed the only reason to ever pass by reference would be to modify something and continue using it after the function has returned? Doesn't sound very well thought of. –  Dec 13 '14 at 23:01
  • Well, I guess it takes 20 years of evolution to make a language which is versatile, efficient and most importantly - allows you to write a program that will not compile unless it's logically correct. :-) – Richard Hodges Dec 13 '14 at 23:02
  • ... or needlessly bloated and overcomplicated with few next to no lessons taken from previous missteps in its design, and requiring people to abandon logic, reason and sanity to conform to its oddities :) –  Dec 13 '14 at 23:05
  • :-) I've been writing software for 30 years. For me c++14 is the most pleasing and efficient way to develop software I have ever encountered. It's worth mentioning that it takes a long time to learn the subtleties of the language, and longer still to learn why they are necessary. But it's worth saying that c++ is an evolving language - you can get involved and help shape it by joining the various interest groups. Remember that it evolved from c - which is basically one step up from a macro assembler :-) – Richard Hodges Dec 13 '14 at 23:12
  • Well, you've gotten used to it alright :) 30 years are enough to learn how to walk on hands, and do everything you use your hands for with your feet. I do not argue C++ is not evolving, I am just not very pleased by its power to complexity ratio. The standards has grown like double since before C++11, but the language did not become twice as fast, much less any easier - on the contrary, it because far more complex. What is a more evolved vehicle - one that anyone could drive or one that requires you to be electrical and mechanical engineer and a physics, math and chemistry wiz? –  Dec 13 '14 at 23:44
  • But I can see why this might be beneficial and desired for people in your position. Making it more complex makes it a fancy commodity, less people use it, keeping the wages up and making sure you don't find yourself unemployed, losing your livelihood to some young punk ;) But that doesn't make the language better, it only makes it better for those it already ensnared and "traumatized" to the point of getting acclimated to it. I reckon there are much better ways to solve problems solved in C++11 and C++14, but it's not what the STD committee is doing, probably for that exact reason - niche. –  Dec 13 '14 at 23:51
  • oh the software industry has tried many times to kill c++ and replace it with their own proprietary solution. The fact is that c++ would not survive unless it was the best solution in some cases. Historically I've written whatever language I've been paid to write in, but my first choice, and the choice of the firm I now head up is c++14 - because in my view it offers the cheapest way to market, by virtue of disallowing logic errors at compile time. Each logic error that makes it into the code is a disaster. c++ is the best tool I know to avoid them. – Richard Hodges Dec 14 '14 at 00:49
  • Do you have a reference for your claim about Stroustrup's reasoning? – user207421 Dec 14 '14 at 00:52
  • @EJP - I am deducing this from the behavior and vested interests of the standard committee though my logic skills, that's just the kind of person I am, for me what people do matters more than what people say, especially when the two don't coincide, even more so when those are on the two opposite extremes. Do you have a reference proving me wrong? –  Dec 14 '14 at 01:14
  • i don't have a direct reference, but here is a breadcrumb trail: http://compgroups.net/comp.lang.c++.moderated/rationale-of-non-const-reference-to-t/171656 You could of course always email Bjarne about it and ask him to add an FAQ about it on his web page: http://www.stroustrup.com/ – Richard Hodges Dec 14 '14 at 01:54