5

In the cpprefernce section: Value categories, it states that "the member of object expression, where a is an rvalue and m is a non-static data member of non-reference type" is an xvalue. In the standard (I found in: N4140, the draft C++14 standard) it states (on page 87) that "An expression is an xvalue if it is... a class member access expression designating a non-static data member of non-reference type in which the object expression is an xvalue."

I wanted to check my understanding of this with the following code:

struct B { // B for Boring...
  int i{0};
};

template <typename T> struct V {
  V() { cout << "V" << endl; }
};

template <typename T> struct V<T &> { // Partial Specialization
  V() { cout << "V&" << endl; }
};

template <typename T> struct V<T &&> { // Partial Specialization
  V() { cout << "V&&" << endl; }
};

int main() {
  int i{1};
  V<decltype((1))> v1;       // V
  V<decltype((i))> v2;       // V&
  V<decltype((move(i)))> v3; // V&&

  V<decltype((B().i))> v4;       // V, why not V&&?
  V<decltype((move(B()).i))> v5; // V&& as expected
}

If my calculations are correct, then B().i is a "member of object expression, where [B()] is an rvalue," and according to the reference that expression should be an xvalue, and the type returned by decltype should be int&&. Note that I'm using gcc version 7.3.0 (Ubuntu 7.3.0-27ubuntu1~18.04), and I tried it with no flags and with -std=c++14.

(To be clear, I'm also checking my understanding of "decltype" here, but the standard I previously linked to and the reference agree verbatim and quite clearly on the behavior of decltype).

Sorry, that wasn't a question... Am I right? Should the reference be updated to clarify this behavior?

Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
Nathan Chappell
  • 2,099
  • 18
  • 21
  • 4
    This seem to be a (fixed) bug in gcc, [clang](https://wandbox.org/permlink/iA6aMIex6mbcHvyj), as well as [newer gcc](https://wandbox.org/permlink/ZB9xe1kEjSixXTwv) print `V&&` as expected – user7860670 Nov 05 '18 at 19:28
  • 1
    The text you quote from N4140 doesn't apply to `v4`, since the object expression `B()` is a prvalue, not an xvalue. And notes are non-normative anyway. The actual rule is in [expr.ref]/4.2 "If `E1` is an lvalue then `E1.E2` is an lvalue; otherwise `E1.E2` is an xvalue." – M.M Nov 06 '18 at 03:23

1 Answers1

1

Yes, you are correct this is a bug, this looks similar to Is f().a[0] an xvalue? but this case dealt speifically with arrays which had a carve out in [expr.sub]p1 which said the result was an lvalue but deemed a defect. Otherwise the same logic applies.

We can see it was fixed in clang 4.0 and if we check clang < 4.0 live, we obtain the same non-conforming results you see from gcc less than the most HEAD but clang 4.0 and > live this is also fixed.

Also note the problem from the Is f().a[0] an xvalue? is also only fixed in gcc HEAD.

Also note that [expr.ref]p4.2

... If E1 is an lvalue, then E1.E2 is an lvalue; if E1 is an xvalue, then E1.E2 is an xvalue; otherwise, it is a prvalue...

changed after C++11 to this:

... If E1 is an lvalue, then E1.E2 is an lvalue; otherwise E1.E2 is an xvalue. ...

It looks like the change was originally part of DR 616 although what it has to do with indeterminate values I am not sure.

Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
  • Thanks. In case it matters to anyone, I'm using Ubuntu 18.04.1 and after upgrading gcc using apt it gives the same (incorrect) behavior. I'll also mention that I was using an older standard, because I'm trying to get "up to speed" with c++14 before moving to c++17, but in a newer standard (which I just looked up) there is significantly different language due to the introduction of "Temporary Materialization." (See page 88 of http://open-std.org/JTC1/SC22/WG21/docs/papers/2017/n4659.pdf). – Nathan Chappell Nov 05 '18 at 20:51