This is an unofficial snapshot of the ISO/IEC JTC1 SC22 WG21 Core Issues List revision 115d. See http://www.open-std.org/jtc1/sc22/wg21/ for the official list.

2024-10-26


2813. Class member access with prvalues

Section: 7.6.1.5  [expr.ref]     Status: DRWP     Submitter: Barry Revzin     Date: 2023-08-28

[Accepted as a DR at the March, 2024 meeting.]

Subclause 7.6.1.5 [expr.ref] paragraph 2 specifies:

For the first option (dot) the first expression shall be a glvalue. ...

This provision forces a temporary materialization conversion (i.e. a copy) per 7.2.1 [basic.lval] paragraph 7:

Whenever a prvalue appears as an operand of an operator that expects a glvalue for that operand, the temporary materialization conversion (7.3.5 [conv.rval]) is applied to convert the expression to an xvalue.

However, this is limiting in the case that an explicit object member function with a by-value object parameter is invoked for a non-copyable class object, for example:

  struct X {
    X() = default;

    X(const X&) = delete;
    X& operator=(const X&) = delete;

    void f(this X self) { }
  };

  void f() {
    X{}.f();   // OK?
  }

The example ought to be well-formed.

Proposed resolution (reviewed by CWG 2024-02-16) [SUPERSEDED]:

  1. Change in 6.7.7 [class.temporary] paragraph 2 as follows:

    ... [Note 3: Temporary objects are materialized: end note]
  2. Change in 7.6.1.3 [expr.call] paragraph 6 as follows:

    When a function is called, each parameter (9.3.4.6 [dcl.fct]) is initialized (9.4 [dcl.init], 11.4.5.3 [class.copy.ctor]) with its corresponding argument. If the function is an explicit object member function and there is an implied object argument (12.2.2.2.2 [over.call.func]), the list of provided arguments is preceded by the implied object argument for the purposes of this correspondence. If there is no corresponding argument, the default argument for the parameter is used. [Example 1:
    template<typename ...T> int f(int n = 0, T ...t);
    int x = f<int>(); // error: no argument for second function parameter
    
    end example] If the function is a static member function invoked using class member access syntax, the object expression is a discarded-value expression (7.2.3 [expr.context]). If the function is an implicit object member function, the object expression of the class member access shall be a glvalue and the this parameter of the function (7.5.3 [expr.prim.this]) is initialized with a pointer to the object of the call, converted as if by an explicit type conversion (7.6.3 [expr.cast]). [Note 5: There is no access or ambiguity checking on this conversion; the access checking and disambiguation are done as part of the (possibly implicit) class member access operator. See 6.5.2 [class.member.lookup], 11.8.3 [class.access.base], and 7.6.1.5 [expr.ref]. —end note] When a function is called, the type of any parameter shall not be a class type that is either incomplete or abstract. [Note 6: This still allows a parameter to be a pointer or reference to such a type. However, it prevents a passed-by-value parameter to have an incomplete or abstract class type. —end note] It is implementation-defined whether ...
  3. Change in 7.6.1.5 [expr.ref] paragraph 2 as follows:

    For the first option (dot), if the id-expression is a data member, the first expression shall be a glvalue. For the second option (arrow), the first expression shall be a prvalue having pointer type. ...

CWG 2024-02-16

When a class member access refers to a static data member or a member enumerator, the object expression should also be treated as a discarded-value expression.

Proposed resolution (approved by CWG 2024-03-01):

  1. Change in 6.7.7 [class.temporary] paragraph 2 as follows:

    ... [Note 3: Temporary objects are materialized: end note]
  2. Change in 7.6.1.3 [expr.call] paragraph 6 as follows:

    When a function is called, each parameter (9.3.4.6 [dcl.fct]) is initialized (9.4 [dcl.init], 11.4.5.3 [class.copy.ctor]) with its corresponding argument. If the function is an explicit object member function and there is an implied object argument (12.2.2.2.2 [over.call.func]), the list of provided arguments is preceded by the implied object argument for the purposes of this correspondence. If there is no corresponding argument, the default argument for the parameter is used. [Example 1:
    template<typename ...T> int f(int n = 0, T ...t);
    int x = f<int>(); // error: no argument for second function parameter
    
    end example] If the function is an implicit object member function, the object expression of the class member access shall be a glvalue and the this parameter of the function (7.5.3 [expr.prim.this]) is initialized with a pointer to the object of the call, converted as if by an explicit type conversion (7.6.3 [expr.cast]). [Note 5: There is no access or ambiguity checking on this conversion; the access checking and disambiguation are done as part of the (possibly implicit) class member access operator. See 6.5.2 [class.member.lookup], 11.8.3 [class.access.base], and 7.6.1.5 [expr.ref]. —end note] When a function is called, the type of any parameter shall not be a class type that is either incomplete or abstract. [Note 6: This still allows a parameter to be a pointer or reference to such a type. However, it prevents a passed-by-value parameter to have an incomplete or abstract class type. —end note] It is implementation-defined whether ...
  3. Change in 7.6.1.5 [expr.ref] paragraph 2 as follows:

    For the first option (dot), if the id-expression names a static member or an enumerator, the first expression is a discarded-value expression (7.2.3 [expr.context]); if the id-expression names a non-static data member, the first expression shall be a glvalue. For the second option (arrow), the first expression shall be a prvalue having pointer type. ...