I have recently learnt about pointer to member syntax and then wrote the program shown below that msvc compiles but gcc and clang rejects.
#include <iostream>
#include <type_traits>
#include <concepts>
struct C
{
int foo()
{
return 5;
}
};
int main()
{
int (C::*ptr)() = &(C::foo); //msvc compiles but not gcc and clang
}
I want to know which one is correct. Live demo
CodePudding user response:
The code is ill-formed, MSVC is wrong.
If the operand is a qualified-id naming a non-static or variant member
m
of some classC
, other than an explicit object member function, the result has type “pointer to member of classC
of typeT
” and designatesC::m
.Otherwise, the result has type “pointer to
T
” ...
In &(C::foo)
, (C::foo)
is not a qualified-id (C::foo
would be one), but rather a primary-expression.
Also [expr.unary.op]/4
is even more explicit:
A pointer to member is only formed when an explicit
&
is used and its operand is a qualified-id not enclosed in parentheses.
The rationale behind this is to have a syntax for forming a regular (non-member) pointer, in case you need one (as opposed to a member-pointer):
#include <type_traits>
struct A
{
int x;
};
struct B : A
{
int x;
void foo()
{
auto f = &A::x;
static_assert(std::is_same_v<decltype(f), int A::*>);
auto g = &(A::x);
static_assert(std::is_same_v<decltype(g), int *>);
}
};
MSVC also accepts this code, so I guess they're only being non-conformant for member functions, not data members (which isn't a big deal, since for member functions &(C::foo)
is otherwise always illegal).
CodePudding user response:
This is P2904 which proposes that the program be well-formed.
Note again that as per the current wording the program is ill-formed but the above paper proposes to make it well-formed by removing the exception in the normal precedence rules. Here is the relevant change in the wording as proposed by the paper:
Change in wording
Change [expr.unary.op#4] to as shown below:
A pointer to member is only formed when an explicit &
is used and its operand is a qualified-id not enclosed in parentheses.
[Note 4: That is, the expression &(qualified-id), where the qualified-id is enclosed in
parentheses, does not form an expression of type pointer to member . Neither does. The
expression qualified-id does not form an expression of type pointer to member, be-
cause there is no implicit conversion from a qualified-id for a non-static member function
to the type “pointer to member function” as there is from an lvalue of function type to
the type “pointer to function” ([conv.func]). Nor Neither is &unqualified-id a pointer
to member, even within the scope of the unqualified-id’s class. — end note]