0
votes

[expr.ref]/2:

For the first option (dot) the first expression shall be a glvalue having complete class type. For the second option (arrow) the first expression shall be a prvalue having pointer to complete class type. The expression E1->E2 is converted to the equivalent form (*(E1)).E2; the remainder of [expr.ref] will address only the first option (dot).68 In either case, the id-expression shall name a member of the class or of one of its base classes. [ Note: Because the name of a class is inserted in its class scope (Clause [class]), the name of a class is also considered a nested member of that class.  — end note ] [ Note: [basic.lookup.classref] describes how names are looked up after the . and -> operators.  — end note ]

According to this paragraph, the lvalue-to-rvalue conversion is applied to p in the snippet below. But it is not applied to a. Why does the standard prescribe a glvalue for the first option (dot) and a prvalue for the second option (arrow)?

struct A{ void f() {} };
A a;
A* p = new A;
int main() {
    a.f();
    p->f();
}
1
... why should it be? It's not clear what the point of your question is.Nicol Bolas
I try to remember if I ever cared whether the expression left of . or -> is an LValue or RValue. If neither is overloaded it must be an LValue (or would not produce an address which in turn is absolutly necessary to apply the "offset" of the right expression). The -> may be overloaded. Due to the implementation of the overloaded -> operator an RValue on left side may become sufficient. (However, I struggle to imagine a practical example - probably my lack of fantasy.) Operator . may never be overloaded (its forbidden by the standard).Scheff's Cat
@Scheff: It's perfectly valid to apply . to rvalues.Nicol Bolas
@Scheff: string("foo").c_str(). That's legal, though the return value cannot be used outside of the overall statement that it appears in.Nicol Bolas
@Scheff string("foo") and std::string() are prvalues. At least, they were in C++14.aschepler

1 Answers

3
votes

Bear in mind that prvalues can be converted to xvalues via a temporary materialization conversion [conv.rval]:

A prvalue of type T can be converted to an xvalue of type T. This conversion initializes a temporary object (15.2) of type T from the prvalue by evaluating the prvalue with the temporary object as its result object, and produces an xvalue denoting the temporary object. T shall be a complete type. [Note: If T is a class type (or array thereof), it must have an accessible and non-deleted destructor; see 15.4. — end note] [Example:

struct X { int n; };
int k = X().n;
// OK, X() prvalue is converted to xvalue

end example]

Prior to the introduction of this new prvalue-to-glvalue conversion, C++14 did not have the restriction for the postfix-expression to be a glvalue.

On that note, C++11 was the first revision to feature user-definable, unconstrained rvalue-to-lvalue conversions by means of the (then-)new rvalue reference type: auto&& x = f(); makes the prvalue f() into an xvalue x.