This is the mail archive of the
gcc-bugs@gcc.gnu.org
mailing list for the GCC project.
[Bug c++/50282] pointer-to-member cast works incorrectly
- From: "imzhuli at vip dot qq.com" <gcc-bugzilla at gcc dot gnu dot org>
- To: gcc-bugs at gcc dot gnu dot org
- Date: Mon, 05 Sep 2011 03:12:55 +0000
- Subject: [Bug c++/50282] pointer-to-member cast works incorrectly
- Auto-submitted: auto-generated
- References: <bug-50282-4@http.gcc.gnu.org/bugzilla/>
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=50282
--- Comment #6 from zhuli <imzhuli at vip dot qq.com> 2011-09-05 03:12:55 UTC ---
I guess we have some agreement on HOW gcc works. the following are what i got
through the test-cases:
1.simply using &T4::f, will only get a value of T2::f, as the function f
accessed through T4,is actually defined in T2. NO CONVERSION WILL TAKE PLACE.
2.directly assign &T4::f to an object of type void(T4::*)(void), will
IMPLICITELY cause an conversion from void(T2::*)(void) to void (T4::*)(void),
That means:
If I want to store a pointer-to-member-function, i should use its original
type, as T2::* ,or T4::T2::* which is only verbose but makes no difference in
current g++ implementation), in the case, and that's what i usually have to
have done in my programs (but not in the test-cases).
BUT:
1.This form is anti-literal. becaouse the programmer should always be aware of
where the function is implemented so as to store it in a generic way, or he
should use template.
2.To be more anti-literal, considering the following cases:
assume i implement a function T::f4, and f4 is originally a member-function
of T4, and i give two assignment:
void (T2::*pf2)() = &T4::f ; // 1> OK, because f is implemented in T2
void (T2::*pf42)() = &T4::f4 ; // 2> Error, because f4 is implemented in T4
as we both know how gcc works, and we know the second assignment voilabe the
c++ standard about "No casting from D::f to B::f", so i say we feel no strange
about the result, but IT IS STILL CONFUSING, right ? event option -Wall will
not yield any warning or suggesting about the first line. At least I think such
a warning is much useful than a warning like "unused variable".
So, I can accepte the way gcc implements the conversions, for I just tested
several cases and i can find out which way I can follow. But I have some
suggestions:
For the compiler always knows from the codes that f is accessable through T4,
we can have two forms of assign &T4::f to objects of type T4::*,
1. (void)(T4::*pf4)() = &T4::T2::f, or
2, (void)(T4::*pf4)() = static_cast<void (T4::*)()>(&T2::f) ;
and:
3 (void)(T4::*pf4)() = &T2::f, will yield an warning.
I thinks this will make programmers feel easier and happier.
(In reply to comment #5)
> The problem is is your code, not gcc
> The type of &T4::f is void(T2::*)() not void(T4::*)() so when you cast the
> pointer to void(T4::*)() you are not casting back to the original type.
> You can fix the code by using reinterpret_cast<void(T2::*)()> for the cases
> that use the expression &T4::f, instead of reinterpret_cast<void(T4::*)()>
> For what it's worth, clang gives exactly the same result as g++ for a reduced
> version of your program:
> // define class N with no member,
> // we just need a type void(N::*)() of which an object can hold a
> pointer-to-member-function
> class N {} ;
> // define class T1 & T3 with some member data/functions
> // so that an object of class T4 which derives from T1 & T2 & T3
> // will has its base object of type T2 have different address from the object
> itself.
> class T1
> {
> public:
> char c ;
> int i ;
> } ;
> class T3:virtual public T1
> {
> public:
> int i3 ;
> public:
> virtual void f2() {
> __builtin_printf("Foo3 !! this=%p\n", (void*)this);
> } ;
> } ;
> // define class T2
> // T2 has a memmber function f, which our pointer-to-member-function will point
> to.
> // as T4 derives from T2 as a public base type, this function is an accessable
> from T4 ;
> // the function outputs the value of 'this' pointer, which i expect it always
> points to an object of T2.
> class T2
> {
> public:
> int i2 ;
> public:
> virtual void f() {
> __builtin_printf("Foo2 !! this=%p\n", (void*)this);
> } ;
> } ;
> // define class T4, which simply derives from T1&T2&T3
> class T4:public virtual T1, public T3, public T2
> {} ;
> int main(int, char**)
> {
> T4 t4 ;
> void (N::*pfn)() = 0;
> void(T4::*pf4)() = &T4::f ;
> // this line shows the address of t4 and its base object t4.t2 differ ;
> __builtin_printf("AddressOf t4=%p, t4.t2=%p\n", (void*)(&t4),
> (void*)(&(T2&)(t4)));
> {
> __builtin_printf("\nwhat i expect:\n");
> // the following lines show what i expect to see:
> // no matter what form the function call is, the function tells me the
> address of t4.t2
> t4.f() ;
> (t4.*(&T4::f))() ;
> (t4.*pf4)() ;
> }
> {
> __builtin_printf("\nTestCase1:\n");
> __builtin_printf("pfn assignment: pfn =
> reinterpret_cast<void(N::*)()>(&T4::f) \n");
> __builtin_printf("Function call form: (t4.*reinterpret_cast<void(T4::*)()>(
> pfn )() \n");
> // Case1:
> // pfn is assigned directly from &T4::f,
> // but actually, its value shows that is &T2::f
> pfn= reinterpret_cast<void(N::*)()>(&T4::f) ;
> // comparing with the result i memtioned above, this is not what i want.
> (t4.*reinterpret_cast<void(T4::*)()>(pfn))() ;
> }
> {
> __builtin_printf("\nTestCase2:\n");
> __builtin_printf( "pfn assignment: pfn =
> reinterpret_cast<void(N::*)()>(pf4) \n");
> __builtin_printf("Function call form: (t4.*reinterpret_cast<void(T4::*)()>(
> pfn )() \n");
> // Case2: pfn is transfromed from pf4, which is defined as type
> void(T4::*)(), and its value
> // has been correctly assigned as &T4::f, not &T2::f
> pfn = reinterpret_cast<void(N::*)()>(pf4) ;
> // this time, the function call works correctly
> (t4.*reinterpret_cast<void(T4::*)()>(pfn))() ;
> }
> {
> __builtin_printf("\nTestCase3:\n");
> // Case3:
> // this case exactly follows the standered draft, if anyone might say
> // in the above casese i used a lvalue(pfn),
> // this time, i have rvalue only, but it's obviously not functioning.
> __builtin_printf("Function call form: (t4.*reinterpret_cast<void(T4::*)()>(
> reinterpret_cast<void(N::*)()>(&T4::f)))() \n");
> (t4.*reinterpret_cast<void(T4::*)()>(
> reinterpret_cast<void(N::*)()>(&T4::f)
> ))() ;
> }
> }
> When the 1st and 3rd tests are altered to use reinterpret_cast<void(T2::*)()>
> it gives the results you expect
> so I think this is invalid