1

Why should not the rule for class member access with an xvalue object expression [expr]/7.3 apply to reference types?

Together with [expr.ref]/4

"If E2 is declared to have type “reference to T”, then E1.E2 is an lvalue; the type of E1.E2 is T."

information on expiration is changed without obvious reason.

Anybody knows the reason for this decision?

#include <utility>

int i;
struct { int && m; } a { std::move (i) };
typedef decltype ((std::move (a).m)) M; 

// M is int &, not &&
static_assert ((std::is_same <M, int &>::value), ""); 

int main () {}

I guess why I'm bothered in the first place is that it's breaking the trivial solution to forwarding class members:

template <typename U> auto f (U && u) 
 -> decltype ((static_cast <U &&> (u).m))
{
    return     static_cast <U &&> (u).m;    
}

without any apparent alternatives, except resorting to more complicated type traits solutions.

2 Answers2

1

If you consider the differences between lvalues and glvalues, and what it makes sense for a struct X { T& r; }; member to allow to happen to the referenced value:

  • lvalues can never be moved from, so you can't accidentally move from the variable referenced by the member, which is safer given the same variable may be referenced from many places and they're presumably meant to cooperate/coordinate in some way: move semantics are not cooperative, as the left-behind value's indeterminate

  • lvalues can have their address taken: why shouldn't you be able to get the address of the referenced variable?

  • you can create new references to the lvalue: making them refer to the same variable X::r references

Tony Delroy
  • 102,968
  • 15
  • 177
  • 252
0

This is consistent with the treatment of id-expressions that are not the right operand of a class member access expression ([expr.prim.general]/8):

int&& i = 0; // i is an lvalue
void f(int&& j) { /* j is an lvalue */ }
struct S { int&& k; void g() { /* k is an lvalue */ } };

In particular, note the last case; it would be highly confusing for k to designate an lvalue within member functions of S but for s.k to designate an xvalue outside.

As to why id-expressions are treated as lvalues even when they have declared type rvalue reference to T, this has been answered at length before; see e.g. On how to recognize Rvalue or Lvalue reference and if-it-has-a-name rule.

Community
  • 1
  • 1
ecatmur
  • 152,476
  • 27
  • 293
  • 366
  • Maybe a better formulation of my question would be why the rule for class member access with an xvalue object expression [expr]/7.3 should not apply to reference types? Together with [expr.ref]/4 mentioned above, information on expiration is changed without obvious reason. Improving my original question with this. – listcrawler Mar 10 '16 at 12:46
  • @listcrawler if a class has a member of reference type, it is presumably referring to some object outside the class, so the value category of the object expression has no bearing on the lifetime on the referent. – ecatmur Mar 10 '16 at 13:39
  • That's a valid remark, putting class members of reference type back in the if-it-has-a-name bunch. I guess I'll put this to rest for now, updating my original questing with a note on member forwarding, why it bothered me in the first place. – listcrawler Mar 10 '16 at 15:09