Description
Reference (section label): [expr.ref]
Issue description: [expr.ref]/2 currently starts:
For the first option (dot) the first expression shall be a glvalue.
This means that in X().f()
, because X()
is a prvalue, the temporary materialization conversion is applied ([basic.lval]/7). Up until C++23, this detail wasn't significant since for any non-static member function f()
, f()
's object parameter would have had reference type, so there was no other real alternative.
But since C++23, with deducing this, we can now have non-static member functions that take their explicit object parameter by value. Which means that this example (from Johel Ernesto Guerrero Peña) is ill-formed:
struct X {
X() = default;
X(const X&) = delete;
X(X&&) = delete;
X& operator=(const X&) = delete;
X& operator=(X&&) = delete;
void f(this X self) { }
};
void f() {
X{}.f();
}
X{}.f()
currently forces a temporary materialization conversion, which means that the explicit object parameter self
is initialized not from the prvalue X{}
(which would work) but rather from an xvalue, which does not.
But taking a function pointer to &X::f
and invoking it with X{}
would of course be fine. The more convenient direct member syntax should also be fine, and indeed P0847 contained motivating examples of using by-value member functions that take advantage of guaranteed copied elision.
Suggested resolution:
Is the quoted sentence actually useful? For the existing non-static member function use-cases, because the object parameter already has reference type, the temporary materialization conversion will already be applied. It seems to simply force an undesired move construction in this case. The resolution might simply be:
For the first option (dot) the first expression shall be a glvalue.For the second option (arrow) the first expression shall be a prvalue having pointer type. The expressionE1->E2
is converted to the equivalent form(*(E1)).E2
; the remainder of [expr.ref] will address only the first option (dot).