From: bart@ingen.ddns.info.invalid   
      
   On Mon, 20 May 2013 12:51:34 -0700, Francis Glassborow wrote:   
      
   > On 20/05/2013 13:40, Daniel Krügler wrote:   
   >>   
   >> Am 20.05.2013 12:16, schrieb Francis Glassborow:   
   >>> On 20/05/2013 00:39, Wil Evers wrote:   
   >>>> I think the lesson to learn from this thread is that a void pointer   
   >>>> obtained from the address of an X must not be cast back to a pointer   
   >>>> to some other type, even if that other type is closely related to X.   
   >>>   
   >>> static_cast should convert the pointer if necessary,   
   >>> reinterpret_cast can fail.   
   >>   
   >> I don't think that static_cast versus reinterpret_cast is relevant in   
   >> the here discussed conversion case. The reason for the observed problem   
   >> really is (as others have said in different words) that the language   
   >> doesn't specify that the conversion sequence (all by means of   
   >> static_cast)   
   >>   
   >> D* d1 -> void* v -> B* b -> D* d2   
   >>   
   >> where B* and D* do have an effective offset different from zero, will   
   >> return an address value d2 that is equal to the original address value   
   >> d1. The constraint is expressed in 5.2.9 p13:   
   >>   
   >> "A prvalue of type “pointer to cv1 void” can be converted to a prvalue   
   >> of type “pointer to cv2 T,” [..] If the original pointer value   
   >> represents the address A of a byte in memory and A satisfies the   
   >> alignment requirement of T, then the resulting pointer value represents   
   >> the same address as the original pointer value, that is, A. The result   
   >> of any other such pointer conversion is unspecified. A value of type   
   >> pointer to object converted to “pointer to cv void” and back, possibly   
   >> with different cv-qualification, shall have its original value."   
   >>   
   >> The behavior of above sequence is undefined, because the assumption   
   >> that b points to the same address value as the original address value   
   >> of d1 is invalid.   
   >   
   > Agreed but it is the step from Base* to Derived* that is the problem.   
      
   It was only a problem because the source pointer did, in fact, *not*   
   refer to a Base (sub-)object.   
      
   > I was merely addressing the Derived* to Base* conversion which AFAIK was   
   > and remains valid if done via a static_cast<> Consider:   
   >   
   > class Base {   
   > // whatever   
   > };   
   > class Derived: public Base (   
   > //whatever   
   > };   
   >   
   > void bar(Base *);   
   >   
   > void foo(){   
   > Base * b1_ptr = new Derived;   
   > Base * b2_ptr = new Base;   
   > bar(b1_ptr); // possible problem with slicing   
      
   As bar() takes its argument as a pointer, there is no risk at all of   
   slicing. The only possible "problem" is that, if Base does not have any   
   virtual functions, then bar() can't take any advantage of what Derived   
   has to offer.   
      
   > bar(b2_ptr); // no problems   
   > }   
   >   
   > Do we now have a problem not just with slicing, but fundamentally that   
   > this what is passed to bar may not even be a Base* ?   
      
   No, there are no problems of any kind in the code you presented.   
   The implicit conversion from Derived (reference or pointer) to Base   
   (reference or pointer) and a static_cast in either direction must take   
   the offset of the Base sub-object within Derived into account.   
      
   This requirement does not hold for reinterpret_cast, or if the conversion   
   goes through an intermediary type (such as void*).   
   It was this latter aspect of which the code from the OP ran afoul. It   
   converted a Derived* to a Base* through a void* in one way, and directly   
   in the reverse way. This created havoc with a nonzero offset of the Base   
   sub-object.   
      
   >   
   > I suspect that we (maybe just me) have got confused. I know that we   
   > cannot cast back from Base* to Derived*.   
      
   Actually, if you know that the pointer refers to a Base sub-object, then   
   a static_cast to Derived (pointer or reference) is completely well-   
   defined (unless they removed that from C++11, which I doubt).   
      
   > However if we use a   
   > static_cast<> we will obtain the address of the Base part of Derived   
   > even if that is different to the address of the derived object.   
   >   
   > The ability to go there and back is limited to polymorphic types and   
   > using dynamic_cast<>.   
      
   It is not as black-and-white as you paint it here, but dynamic_cast<>   
   *does* give you more guarantees if you made an error in remembering which   
   most-derived class your pointer actually refers to.   
   You *can* make the conversion with static_cast<>, but you are on your own   
   if you screw up (as in, UB).   
      
   Bart v Ingen Schenau   
      
      
   --   
    [ See http://www.gotw.ca/resources/clcm.htm for info about ]   
    [ comp.lang.c++.moderated. First time posters: Do this! ]   
      
   --- SoupGate-Win32 v1.05   
    * Origin: you cannot sedate... all the things you hate (1:229/2)   
|