New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[expr.mptr.oper] Clarify pointer-to-member operators. #4733
base: main
Are you sure you want to change the base?
Conversation
@@ -5795,7 +5794,10 @@ | |||
The restrictions on cv-qualification, and the manner in which | |||
the cv-qualifiers of the operands are combined to produce the | |||
cv-qualifiers of the result, are the same as the rules for | |||
\tcode{E1.E2} given in~\ref{expr.ref}. | |||
\tcode{E1.EM} given in~\ref{expr.ref}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jensmaurer Maybe, we should say that
EM
is a qualified-idT::m
naming a memberm
of classT
declared with typeX
where the member is pointed by the value of the second operand, ignoring mutable specifier(if any).
In order to make the name lookup rule can uniformed work here; for instance, in a derived class, there is a member with the name m
hides the member of the same name declared in the base class. The purpose of this utterance prevents the lookup rule from recklessly starting to perform in the scope associated with the object expression.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think name lookup considerations are not really relevant here. I say "EM must name a member of T" below; plus EM has a unique name (it's hypothetical / invented), so there is no hiding in view.
All that's relevant is the type of EM and the fact it's non-static and non-mutable; everything else falls out of the expr.ref rules.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would expect that the expression E1.EM
can additionally state what the result is, after all, [expr.mptr.oper#2] merely says "The result is an object or a function of the type X", but it didn't specify which object or function is it, which needs the help of the relevant rule about the result of E1.EM
cited below. Except for that, the original utterance is ok for stating type.
If E2 is a non-static data member..., the expression designates the corresponding member subobject of the object designated by the first expression.
The type of E1.E2 is the type of E2 and E1.E2 refers to the function referred to by E2.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, I think the paragraph [expr.mptr.oper#5] should be changed to that
The result of E1.*E2 is determined by E1.EM([expr.ref]), where EM is a qualified-id
T::m
naming a memberm
of class T to which the value of E2 refers and the mutable specifier in the declaration of the member is ignored(if any). Except that If the value of E2 refers to a non-static member function that is virtual([class.virtual]), the function designated by E1.*E2 is the final overrider in the dynamic type of the object expression(i.e, the type of the result remains determined by E1.EM).
Now, the [expr.mptr.oper#5] specifies all the respects regard the result of expression E1.*E2
(e.g, which object or function does the result denote).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why can't we just say "E2 points to" instead of "the value of E2 points to"? It's a pointer, it points to things.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
E2 is an expression, so it presumably it has a value.
That said, I note that @xmh0511 also laments the absence of a specification what the result value actually is, so some massaging to the original wording is probably necessary. I would like to avoid talking in "identifiers" for anything but finding out the cv-qualification of the result type, if we can help it. There's too much name lookup / virtual function etc baggage around "identifier".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I want to use "the value of E2" to avoid a similar issue mentioned in #4662 (comment). But, it seems you're right that the meaning is unambiguous if using "E2 points to". However, the benefit of using "the value of ..." is that it implies the lvalue-to-rvalue
conversion shall be applied to the operand(if necessary), which is also under-specified in many sections in [expr].
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jensmaurer Not strictly, a pointer to member records the information of the entity and its class, hence I think that we use T::m
is equivalent to use the entity pointed to by the pointer to member in most respects. The exception is the virtual function, which has been listed in the proposed wording. IMHO, the wording could probably cover all cases.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider the second operand may result from Pointer-to-member conversions, I think we should separate [expr.mptr.oper#2] into two parts; One part phrases type of the result, the other phrases the entity of the result. Since the wording about the type of the result in the current changed file is ok, we should pay attention to the entity designated by the result.
The entity designated by E1.*E2 is determined by E1.EM([expr.ref]), where EM is a qualified-id
T::m
naming the member to which the value of E2 refers, except that If the value of E2 refers to a virtual function, the function designated by E1.*E2 is the final overrider in the dynamic type of the object expression.
Since the relevant rule defined in [conv.mem]
The result of the conversion refers to the same member as the pointer to member before the conversion took place, but it refers to the base class member as if it were a member of the derived class. The result refers to the member in D's instance of B.
So, even if the operand E2 is taken from the result of Pointer-to-member conversions, its value still refers to the original member, which is the entity that E1.*E2 designates.
@jensmaurer Any thoughts on this proposal? Would you like any other input? |
@tkoeppe, I think I want CWG review for this eventually. |
Could you please rebase? |
Fixes #4703