2
votes

The following does not compile:

#include <iostream>
using namespace std;

int x = 5;
int && f () { return std::move(x); }
int g(int & y) { return y; }

int main() {
    g(f());
    return 0;
}

It's clear to me why prvalues (unnamed temporaries) do not bind to non-const lvalue references -- it does not make sense to modify them, as they will soon disappear. Yet why do xvalues not bind to non-const lvalue references?

If a function returns int &&, the referenced object can't be temporary, otherwise we would get a dangling reference. Hence if an int && is returned, that's, in my understanding, a reference with the additional guarantee that it's safe to move from it.

Edit: Wording corrected: "Values" bind to "references" and not vice versa.

Second edit: The following compiles -- I do not see the conceptual difference, besides y now being an lvalue. Yet it still references x. I understand why this should compile and the above shouldn't, by the language specification. I do not, however, understand the reason behind it. Why does mere aliasing change the picture?

#include <iostream>
using namespace std;

int x = 5;
int && f () { return std::move(x); }
int g(int & y) { return y; }

int main() {
    int && y = f(); // reference!
    g(y); // compiles

    // check that y indeed references x
    y = 7;
    std::cout << x << std::endl; // prints 7, of course
    return 0;
}

Third edit: In short, what's the idea behind not allowing

int && f() { ... }
int g (int & y) { ...}
g(f());

yet allowing

int && f() { ... }
int g (int & y) { ...}
int & k (int && y) { return y; }
g(k(f()));
1
re "can't be temporary", consider auto f( int&& x ) -> int&& { return move(x); }Cheers and hth. - Alf
Note that given struct A { int x; };, A().x is an xvalue, per CWG DR 616.T.C.
@Cheersandhth.-Alf I think (but correct me if I'm wrong) that the OP is not saying that it's impossible for xvalues to refer to temporaries, but rather that your example function already shouldn't be written like that, that it is a poor function definition as it allows soon-to-be-dangling references to be returned.user743382
I don't think the term "binding to an xvalue" makes sense. Values bind to references, that's the only related concept that exists in the language.Kerrek SB
@hvd: That would make the OP's reasoning circular.Cheers and hth. - Alf

1 Answers

5
votes

Because that would make a mess.

The whole point of non-const lvalue references is that they are aliases for non-temporary objects whose lifetime is already being managed by some other means. The introduction of rvalue references — and, crucially, std::move — was specifically to create a new "class" of references whose binding signifies that the referent is most likely "safe" to move from.

If these referents also were able to bind to a simple T&, you'd have a slew of ambiguous conversion errors and nobody would know what to do. You could give such conversions a lower rank, but I feel that this would still be extremely confusing.

As such, you're looking at it backwards. Ask yourself instead why xvalues don't bind to non-const lvalue references.