2

Why reference can not capture temporary value while const reference and rvalue reference can capture and prolong object life. In other words while two first lines are legal but third not:

const string &a = string("a");
string &&b = string("b");
string &c = string("c"); // why illegal?
Trismegistos
  • 3,821
  • 2
  • 24
  • 41
  • What are you going to do with it? Modify it and propagate the changes back to no one? – chris Jul 29 '14 at 21:49
  • @chris I would use it for optimization. – Trismegistos Jul 29 '14 at 21:50
  • What optimization would that be? – chris Jul 29 '14 at 21:52
  • The duplicate doesn't address the part about rvalue references that this question asks. – Howard Hinnant Jul 29 '14 at 21:53
  • @chris what are you going to do rvalue reference which can be used in place of regular reference? Why standard do not ask such question? You can still use rvalue reference no only for std::move like operation but also as a replacement for regular reference in context I have presented. You const reference also prolong lifetime. Why prolong lifetime of const object but not of non const? – Trismegistos Jul 29 '14 at 21:58
  • Rvalue references are specifically there for move semantics and perfect forwarding. Non-const references to temporaries doesn't buy anything. – chris Jul 29 '14 at 21:59
  • @chris rvalue reference *can* be used for move semantics. It is not only posibility. Another one is replacing standard reference in context I provided so why they are allowed to do this while refs are not? – Trismegistos Jul 29 '14 at 22:02
  • @chris It can be useful for, perhaps overly, shortened code. Consider `ofstream("file.txt") << x;` where x is a user class. This is invalid because the operator takes the stream as non-const reference. – Neil Kirk Jul 29 '14 at 22:33
  • @NeilKirk, [Not sure I understand.](http://coliru.stacked-crooked.com/a/407a365b67c0d817) – chris Jul 29 '14 at 22:36
  • @chris You hit the library rvalue stream overload for `operator<<`. – T.C. Jul 29 '14 at 22:53
  • I what? Then how did the effects of my overload (for my own type) happen? I don't know of any rvalue overload of `operator<<` that just delegates to an lvalue overload. – chris Jul 29 '14 at 22:58
  • @chris That works in C++11 but not prior. I don't have any C++11 updated example. Maybe it is no longer useful for anything. – Neil Kirk Jul 29 '14 at 23:10
  • 1
    @chris It's the last overload on [this page](http://en.cppreference.com/w/cpp/io/basic_ostream/operator_ltlt2). The standard quote specifying it can be found [here](http://stackoverflow.com/a/24495582/2756719). – T.C. Jul 30 '14 at 00:18
  • @T.C., Fun. I suppose it was put in for that purpose. – chris Jul 30 '14 at 00:34

1 Answers1

9

Quoting from N1377

Bjarne in his excellent text "The Design and Evolution of C++" discusses the motivation for prohibiting the binding of an rvalue to a non-const reference in section 3.7. The following example is shown:

void incr(int& rr) {rr++;}

void g()
{
    double ss = 1;
    incr(ss);
}

ss is not incremented, as a temporary int must be created to pass to incr(). The authors want to say right up front that we agree with this analysis 100%. Howard was even bitten by this "bug" once with an early compiler. It took him forever to track down what was going on (in that case it was an implicit conversion from float to double that created the temporary).

This rationale (for not binding rvalues to non-const (lvalue) references) held from the dawn of C++, up until C++11 (2011). However the same rationale does not apply to lvalue references to const:

It is "safe" to bind a temporary to an lvalue reference to const because the compiler will tell you if you accidentally make a "useless" modification to this temporary.

So why is it "safe" to bind an rvalue to an rvalue reference?

Again quoting from N1377:

Having said that, we would like to add: You don't ever want to bind a temporary to a non-const reference ... except when you do.

A non-const reference is not always intended to be an "out" parameter. Consider:

template <class T>
class auto_ptr
{
public:
    auto_ptr(auto_ptr& a);
    ...
};

The "copy" constructor takes a non-const reference named "a". But the modification of "a" is not the primary goal of this function. The primary goal is to construct a new auto_ptr by pilfering "a". If "a" happens to refer to an rvalue, this is not a logical error!

In summary, sometimes you want to modify an rvalue, and sometimes you don't. The different types of references allow the programmer to tell the compiler which situation they are in.

The bindings in your question are a logical conclusion of the bindings motivated by N1377.

The move semantics proposal did put effort into coming up with a solution that did not require language changes (i.e. the introduction of the rvalue reference). However library only solutions were unsatisfactory as the resulting syntax for building things like move constructors was overly complex.

Howard Hinnant
  • 206,506
  • 52
  • 449
  • 577
  • 1
    Maybe add that the inherent dangers in using normal references for move-semantics was the rationale for introducing r-value references at all, and on doing that `auto_ptr` was immediately deprecated as unsafe? – Deduplicator Jul 29 '14 at 22:06
  • @Deduplicator: Thanks for the suggestion. I added another paragraph, see what you think. Happy to try again if my response isn't yet clear. – Howard Hinnant Jul 29 '14 at 22:12
  • Example code from N1377 can now be broken by using rvalue reference. So they introduced described dangerous behaviour anyway which is a cost for introducing move semantics. – Trismegistos Jul 29 '14 at 22:12
  • 1
    @Trismegistos If you use a rvalue reference, then you know that you are taking a temporary (or something the caller wants you to treat as a temporary). – T.C. Jul 29 '14 at 22:13
  • @Trismegistos: You never want to modify a temporary ... except when you do. :-) – Howard Hinnant Jul 29 '14 at 22:14
  • @T.C. One can be tempted to use rvrefs instead of refs everywhere because they extend refs so such replacement is legal but dangerous. – Trismegistos Jul 29 '14 at 22:14
  • @Trismegistos But they don't bind to lvalues :) – T.C. Jul 29 '14 at 22:15
  • @Trismegistos: There are lots of ways to hang yourself if you are determined to do so. That being said, if you are arguing for a language redesign, may I suggest using one of these forums: https://isocpp.org/forums – Howard Hinnant Jul 29 '14 at 22:17
  • @T.C. You are right. There are still many surprises for me in those new features. – Trismegistos Jul 29 '14 at 22:17
  • @HowardHinnant Problem is in c++ it is easy to hang yourself even if you are not determinded to do so. – Trismegistos Jul 29 '14 at 22:18
  • @Trismegistos: Perhaps. `auto_ptr` implemented move semantics prior to the introduction of rvalue references. And implementing that was **hard**. – Howard Hinnant Jul 29 '14 at 22:22