JohnB
JohnB

Reputation: 13713

Why don't xvalues bind to non-const lvalue references?

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()));

Upvotes: 2

Views: 279

Answers (1)

Lightness Races in Orbit
Lightness Races in Orbit

Reputation: 385104

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.

Upvotes: 5

Related Questions