Created on 2023-08-28.00:00:00 last changed 4 months ago
Proposed resolution (approved by CWG 2024-03-01):
Change in 6.7.7 [class.temporary] paragraph 2 as follows:
... [Note 3: Temporary objects are materialized:—end note]
- when binding a reference to a prvalue (9.4.4 [dcl.init.ref], 7.6.1.4 [expr.type.conv], 7.6.1.7 [expr.dynamic.cast], 7.6.1.9 [expr.static.cast], 7.6.1.11 [expr.const.cast], 7.6.3 [expr.cast]),
- when performing certain member
accessaccesses on a class prvalue (7.6.1.5 [expr.ref], 7.6.4 [expr.mptr.oper]),- when invoking an implicit object member function on a class prvalue (7.6.1.3 [expr.call]),
- ...
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 ...
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. ...
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 (reviewed by CWG 2024-02-16) [SUPERSEDED]:
Change in 6.7.7 [class.temporary] paragraph 2 as follows:
... [Note 3: Temporary objects are materialized:—end note]
- when binding a reference to a prvalue (9.4.4 [dcl.init.ref], 7.6.1.4 [expr.type.conv], 7.6.1.7 [expr.dynamic.cast], 7.6.1.9 [expr.static.cast], 7.6.1.11 [expr.const.cast], 7.6.3 [expr.cast]),
- when performing certain member
accessaccesses on a class prvalue (7.6.1.5 [expr.ref], 7.6.4 [expr.mptr.oper]),- when invoking an implicit object member function on a class prvalue (7.6.1.3 [expr.call]),
- ...
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 ...
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. ...
[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.
History | |||
---|---|---|---|
Date | User | Action | Args |
2024-07-20 13:52:34 | admin | set | status: dr -> drwp |
2024-04-05 21:43:46 | admin | set | status: ready -> dr |
2024-03-20 14:10:31 | admin | set | status: tentatively ready -> ready |
2024-03-06 20:13:27 | admin | set | messages: + msg7632 |
2024-03-06 20:13:27 | admin | set | status: review -> tentatively ready |
2024-02-17 11:33:47 | admin | set | messages: + msg7612 |
2024-02-17 11:33:47 | admin | set | status: tentatively ready -> review |
2024-02-16 23:25:38 | admin | set | status: review -> tentatively ready |
2023-12-16 09:40:44 | admin | set | messages: + msg7560 |
2023-12-16 09:40:44 | admin | set | status: open -> review |
2023-08-28 00:00:00 | admin | create |