4

This very simple code is allowed:

class s
{
public:
};

const s& tt = std::move(s()); // Also valid without 'const'

But right now I'm wondering why it is allowed..

First we use std::move and mark a rvalue (the temporary) as rvalue reference, but why a lvalue reference can bind to a rvalue reference?

Is it because a rvalue reference is also a rvalue?

What's the rationale (or standard quotes) that causes a lvalue reference to bind to a rvalue reference?? That it can be done I got it.

Edit: msvc2015 allows for nonconst lvalue references to bind to rvalue references, the question remains for const lvalue references binding to rvalue references. Sorry, I should have specified the compiler I used.

Dean
  • 6,610
  • 6
  • 40
  • 90
  • 1
    I find this question a bit hard to parse because you are using the terminology the wrong way round: In `T & t = u;`, we say that "(the reference) `t` binds to the value `u`." Moreover, note that references always bind to *values*; a reference does not "bind to another reference". Reference types are always connected with *variables* (or functions), never with expressions. In the usual use of terminology, the question might become "Why do lvalue references bind to rvalues?", which is a good question (with a simple answer). – Kerrek SB Dec 01 '16 at 11:11
  • @KerrekSB My bad, I edited the question to (hopefully) use the right terminology – Dean Dec 01 '16 at 11:15
  • 5
    it [can not compile](http://coliru.stacked-crooked.com/a/3aca61d34a714765) without `const` – apple apple Dec 01 '16 at 11:31
  • I don't believe your premise: A non-const lvalue reference does *not* bind to an rvalue. Do you have an example where you think that works? – Kerrek SB Dec 01 '16 at 11:32
  • Are you using MSVC, which has an evil extension that allows you to bind a non-const lvalue reference to an rvalue? – cpplearner Dec 01 '16 at 12:11
  • arrghhh damn msvc2015! well at least the question remains for const lvalue references: why do they bind to rvalue references? – Dean Dec 01 '16 at 12:29
  • Probably related question: http://stackoverflow.com/questions/39718268/why-do-const-references-extend-the-lifetime-of-rvalues – dutchdukes Dec 01 '16 at 12:39
  • @dutchdukes yes but not for rvalue references – Dean Dec 01 '16 at 13:21
  • @KerrekSB I edited the question again. Hopefully the bold part should clear all doubts on everyone's side – Dean Dec 01 '16 at 15:12
  • @Dean: Well, except for the fact that you "bind to values", it's sufficiently clear now. (The answer is of course that you want function calls to work both efficiently and conveniently, so you want a reference for efficiency and a call with an rvalue for convenience.) – Kerrek SB Dec 01 '16 at 18:30
  • @KerrekSB I've one question regarding your sentence *Reference types are always connected with variables (or functions), never with expressions.*. Aren't *values* (e.g., lvalues, rvalues, ...) actually *expressions*? If so, could we say then that *reference types bind to **expressions***? Thank you. – JFMR Mar 15 '19 at 14:27
  • @KerrekSB Well, isn't it actually the other way around? That is, *a value/expression binds to a reference*. – JFMR Mar 15 '19 at 14:41
  • 1
    @ElProfesor: hmm... the Standard usually says "the reference _is bound to_ the expression", actually. – Kerrek SB Mar 15 '19 at 23:06
  • @KerrekSB Thank you. Since your sentence is written in passive voice, the corresponding sentence in active voice would be *the expression binds to the reference*, wouldn't it? Actually, I learnt that terminology from [a comment of yours](https://stackoverflow.com/questions/34484246/why-dont-xvalues-bind-to-non-const-lvalue-references#comment56710676_34484246). – JFMR Mar 16 '19 at 12:28
  • 1
    @ElProfesor: Yes, I suppose that's true :-) – Kerrek SB Mar 17 '19 at 00:05

1 Answers1

6

Rvalue references are implicitly converted to rvalues (more specifically, to xvalues) as one of the standard conversions (chapter 4 of the C++ standard):

The effect of any implicit conversion is the same as performing the corresponding declaration and initialization and then using the temporary variable as the result of the conversion. The result is an lvalue if T is an lvalue reference type or an rvalue reference to function type (8.3.2), an xvalue if T is an rvalue reference to object type, and a prvalue otherwise

Rvalues (including xvalues) can be bound to const lvalue references so that you can pass a temporary to a function with such a parameter:

void foo(const bar &a);
// ...
foo(bar());

(A temporary is an rvalue; in this case, the result of bar() is an rvalue). There is no reason not to allow this, since the temporary always lives as long as the containing expression - in this case, the function call - and so it will not create a dangling reference inside foo.

This means it is always possible to adjust the signature of a function fun(bar) to fun(const bar &) - and change the implementation accordingly of course! - since temporary arguments will still be accepted, and the semantics should be the same from the perspective of the caller; const implies the object won't be modified, which is also the case if it is passed by copy.

Non-const references are not allowed; one practical reason is because they imply that the value should be modified in some meaningful way, and if the value is a temporary, this would be lost. However, you can convert an rvalue to an lvalue if you really want to do so, with some caveats, as described in this answer to another question.

Allowing an rvalue to bind to a const lvalue reference, other than allowing temporary arguments to be passed by reference, is also good for cases where the exact parameter type is not known but you want to allow move semantics if it is possible. Suppose that I am calling a function that could be defined as foo2(const bar &) or foo2(bar) and which in the former case may or may not have an overload foo2(bar &&), and I want to allow move semantics to be used if possible (assuming that a foo2(bar &&) overload will use move semantics in its implementation); I can safely use std::move to create an rvalue since it will apply in either case. This example might seem a little contrived, but it is the sort of thing that can come up quite a bit when writing templates. In code:

bar bb = bar();
foo2(std::move(bb));
// above is legal if foo2 is declared as either:
//    foo2(bar)
//    foo2(const bar &)
// and in latter case calls overload foo2(bar &&) if it is defined.

In the case of other rvalue-to-lvalue-reference assignments involving a temporary, the lifetime of the temporary is extended to that of the reference, so that dangling references are not created even in contexts other than parameter passing:

const bar &b = bar(); // temporary lifetime is extended

In the above, the bar object will not be destroyed until the reference b goes out of scope.

davmac
  • 20,150
  • 1
  • 40
  • 68
  • This is a good answer but I feel it misses the point of my question: **what's the rationale (or standard quotes) that causes a lvalue reference to bind to a rvalue reference??** That *it can be done* I got it. – Dean Dec 01 '16 at 15:11
  • @Dean it's so you can pass a temporary to a function accepting a `const` lvalue reference, so that in turn there's no semantic difference from the point of view of the caller between `foo(const bar &)` and `foo(bar)`. It's all in the answer. – davmac Dec 01 '16 at 15:15
  • @Dean and also: "There is no reason *not* to allow this" ... i.e. why do you think an rvalue *shouldn't* be able to bind to an lvalue reference? – davmac Dec 01 '16 at 15:16
  • I'm talking about rvalue *reference*s. Is a rvalue reference an rvalue? If so, that is the answer I'd like to read – Dean Dec 01 '16 at 15:17
  • 2
    @Dean rvalue references are implicitly converted to rvalues (specifically, to xvalues). The part of the standard you want is "4. Standard Conversions" [conv]. _The effect of any implicit conversion is the same as performing the corresponding declaration and initialization and then using the temporary variable as the result of the conversion. The result is an lvalue if T is an lvalue reference type or an rvalue reference to function type (8.3.2), **an xvalue if T is an rvalue reference to object type**, and a prvalue otherwise._ – davmac Dec 01 '16 at 15:23
  • On above `foo2(std::move(bb));`, if I pass a temporary instead like `foo(bar())` will it be copied to the parameter or will it be moved? And how things depend on if I have rvalue override of `foo2` or not? If I always have to use `std::move` to evade a copy, what is the use of overriding for rvalue references? – meguli Nov 03 '17 at 15:51
  • @meguli Eg. `foo2(bar())` will call the rvalue-reference override of `foo2` (if there is one), because `bar()` is a temporary and therefore an rvalue. This isn't really about move-vs-copy semantics since `foo2` is not a constructor. You don't need to always use `std::move` for move semantics, but to get move semantics you must both (a) have an rvalue-reference overload for the constructor and (b) supply an rvalue argument, which can be a temporary or an rvalue created via `std::move()`. – davmac Nov 03 '17 at 16:23
  • So the move-vs-copy thing is about whether my `bar` object have a move constructor or not, right? If `bar` had a move constructor and I called `foo2(bar());`, it would move? – meguli Nov 03 '17 at 16:28
  • @meguli in general if you called `foo2(bar())` you would get neither move nor copy, since it would be elided. However if you have an rvalue-reference override of `foo2` then it would be that override that was called (rather than `foo2(const bar &)` or `foo2(bar)`). – davmac Nov 03 '17 at 16:32
  • (actually it would be ambiguous between `foo2(bar &&)` and `foo2(bar)`, but you get the idea). – davmac Nov 03 '17 at 16:46