I used to always copy and paste my code in cppinsights to see where's in my code a conversation is performed implicitly by the compiler. But pointer-to-member standard conversion is not shown on this site.
I am getting more confused when seeing the answer to this question: Downcasting pointer to member leads to undefined behavior. I think the question itself is asked wrongly since, per my knowledge, a pointer-to-member can be safely downcasted, per [conv.mem]/2; but it cannot be upcasted safely except [expr.static.cast]/12 is applied. This question has the following code (edited):
struct B { int a; };
struct D1 : B {};
struct D2 : B { int b; };
void f()
{
int D1::*ptr = &B::a; // #1
int D1::*ptr = &D2::a; // #2
int B::*ptr = &D1::a; // #3
int D1::*ptr = &D2::b; // #4
}
My confusion arises just because I don't know to-what actually ptr
is pointing? to the member of the enclosing object itself? or to the member of the subobject of the enclosing object?
Hence, before posting any questions here, I am turned to a some C++ programmer (actually my retired teacher) to help me and explain the above implicit conversations (if any). And briefly that's what I am getting:
#1- Here, the initializer is a prvalue designating a pointer-to-member B::a
. And because the D1
has B
unnamed subobject, ptr
can point to the a
member of that subobject; in fact, ptr
points to D1::B::a
, where B::a
is a member of B
subobject of D1
. The resulting pointer ptr
refers to the same member of the subobject B
(not D1
) which is actually D1::B::a
; that's because even if D1
has an actual not-inherited member named a
, the resulting pointer still refers to the same member of that subobject.
#2- Here, the initializer is a prvalue designating a pointer-to-member D2::a
. And because the D2
(like D1
) has B
unnamed subobject, ptr
can point to the a
member of that subobject; in fact, ptr
points to D1::B::a
, where B::a
is a member of B
subobject of D1
. The resulting pointer ptr
refers to the same member of the subobject B
(not D1
) which is actually D1::B::a
that's because even if D1
has an actual not-inherited member named a
, the resulting pointer still refers to the same member of that subobject.
#3- Here, the initializer is a prvalue designating a pointer-to-member D1::a
. And because the D1
has B
unnamed subobject, ptr
can point to the a
member of that subobject; in fact, ptr
points to D1::B::a
, where B::a
is a member of B
subobject of D1
. The resulting pointer ptr
refers to the same member of the subobject B
(not D1
) which is actually D1::B::a
; that's because even if D1
has an actual not-inherited member named a
, the resulting pointer still refers to the same member of that subobject.
#4- Here, the initializer is a prvalue designating a pointer-to-member D2::b
. And because the D2
has no D1
subobject can ptr
pointer points to its D1::a
member, the program necessitates this conversion is ill-formed even if static_cast
is used.
Actually, I feel that this explanation is logical and satisfies me, but I think that it has some mistakes and is not 100% true, specifically, paragraph #3.
My Question:
- Are the above explanations entirely true?
Please feel free to correct the paragraphs if they have some mistakes, and answer with the corrected paragraphs, if possible. Thanks, and sorry for taking long.