This is the mail archive of the gcc-bugs@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[Bug c++/50282] pointer-to-member cast works incorrectly


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


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]