3

From 5.2.1.1:

The expression E1[E2] is identical (by definition) to *((E1)+(E2)) [...] except that in the case of an array operand, the result is an lvalue if that operand is an lvalue and an xvalue otherwise.

However, with the code below:

struct S
{
    int arr[5];
};

int main()
{
    int &&r = S().arr[0];
}

both GCC and Clang complain about "rvalue reference cannot bind to lvalue int".

What have I misunderstood? As I understand S() is rvalue, S().arr is xvalue, so S().arr[0] should be xvalue as well and should be able to bind to rvalue refs.

πάντα ῥεῖ
  • 1
  • 13
  • 116
  • 190
ledonter
  • 1,269
  • 9
  • 27
  • @Someprogrammerdude I'm pretty sure it does, cf. the `for(auto &&r : c)` pattern that also works for `std::vector`. Edit: [it does indeed](http://en.cppreference.com/w/cpp/language/lifetime#Temporary_object_lifetime). – Quentin Mar 12 '17 at 17:32
  • 2
    @Someprogrammerdude rvalue references absolutely prolong lifetimes. – Barry Mar 12 '17 at 17:32
  • (rvalue) references prolong lifetimes, but only when a temporary *value* is assigned to them. In this case `arr[0]` returns a reference and the value is an array element, of an array which is a member variable of a temporary. So in this case `r` absolutely does dangle, just not for the reasons originally mentioned. So OP: while this is a compiler bug, this code is still a terrible idea. – Nir Friedman Mar 12 '17 at 17:37
  • 1
    @NirFriedman No, `r` would prolong the lifetime of `S()`: "The temporary to which the reference is bound or the **temporary that is the complete object of a subobject to which the reference is bound** persists for the lifetime of the reference" – Barry Mar 12 '17 at 17:39
  • @Barry No, you are incorrect. I don't know what else to say. Lifetime extension is not transitive. `S` is never assigned to a reference, so why would its lifetime be extended? Working example (had to change to const ref, because coliru's clang/gcc versions have the bug): http://coliru.stacked-crooked.com/a/c8f585b116449088. – Nir Friedman Mar 12 '17 at 17:42
  • @NirFriedman That's a bug. Actual [working example](http://melpon.org/wandbox/permlink/idgCoMX3idK9AsMw). `S`'s lifetime is extended because it's the complete object, per the text I cited. – Barry Mar 12 '17 at 17:44
  • @NirFriedman See [Columbo's answer](http://stackoverflow.com/a/35947572/2069064). – Barry Mar 12 '17 at 17:49
  • @Barry My apologies, I stand corrected. I think this is still a bit dicey though, as once you start binding references to references from temporaries it's still easy to get a dangle, e.g. `const auot& foo = *make_unique();`, or I think (if I understand the wording), even replacing the array with a `vector`. In other words: I learned something new but I probably still won't have things like this in my code. I'd bind the temporary first and then access the member. – Nir Friedman Mar 12 '17 at 17:51
  • @Quentin (and others) you're right. But the example with a ranged-for loop is not a very good one since the lifetime of the container and its contained object would be longer than the loop anyway. – Some programmer dude Mar 12 '17 at 18:02
  • @Someprogrammerdude the pattern I mentionned is useful in the case of containers whose iterators return proxy rvalues instead of references to actual contained object, as `std::vector` does. With `auto &&`, the proxies lifetime is correclty extended to the scope of the loop. – Quentin Mar 12 '17 at 18:09

1 Answers1

5

You are correct, for the reason you cite. S().arr[0] is an xvalue as of DR 1213, so you should be able to bind an rvalue reference to it.

This is gcc bug 79832. Note that clang HEAD compiles this fine.

Barry
  • 286,269
  • 29
  • 621
  • 977