开发者

Are these pointer conversions using static_cast ok?

Suppose we have the follo开发者_C百科wing code:

#include <iostream>

struct A
{
    virtual void f() { 
        std::cout << "A::f()" << std::endl;
    }
};

struct B: A
{
    void f() { 
        std::cout << "B::f()" << std::endl;
    }
};

void to_A(void* voidp) {
    A* aptr = static_cast<A*>(voidp);
    aptr->f();
}

void to_B(void* voidp) {
    B* bptr2 = static_cast<B*>(voidp);
    bptr2->f();
}

int main() {
    B* bptr = new B;
    void* voidp = bptr; 
    to_A(voidp); // prints B::f()
    to_B(voidp); // prints B::f()
}

is this code guaranteed to always work as in the code comments or is it UB? AFAIK it should be ok, but I'd like to be reassured.

EDIT

Ok, so it seems there's a consensus that this code is UB, as one can only cast to the exact type. So, what if the main() changes to:

int main() {
    B* bptr = new B;
    to_A(static_cast<A*>(bptr)); // still prints B::f()
    to_B(bptr); // still prints B::f()
}

is it still UB?


Your first code example invokes undefined behaviour.

You can use a static_cast to reverse a standard conversion of pointer to object type to pointer to void but the result is only guaranteed if the value of the pointer to void being converted back to the original object type is the result of the standard conversion of a pointer to the original type to pointer to void.

Your second code example is OK because you only reverse conversions from pointer-to-type to pointer-to-void by casting back to the original type that the conversion was made from. This is guaranteed in 5.2.9 [expr.static.cast] of the standard (C++03).

... A value of type pointer to object converted to “pointer to cv void” and back to the original pointer type will have its original value.


In this specific case it works, but there are other cases in which it doesn't work.

The problem is the place where you cast the B-pointer to void-pointer to A-pointer. In this case the pointers will all have the same value, but in the following conditions this isn't true anymore:

  • the base class has no virtual methods (therefore, no vptr), but the subclass has virtual methods (I once encountered such a bug in my company's software)
  • the subclass uses multiple inheritance

The only safe way is to cast it exactly back to the same type as where you came from. So if you cast a B-pointer to void-pointer, cast it back to a B-pointer, not to a pointer to another class, even if they belong to the same inheritance tree.


This is actually quite a common thing to try to do, especially in functions that require a C callback, so one has to be careful. A typical C API callback looks like:

void pfnCallback( void * );

In your C++ code you decide to use a base class to always handle this particular callback and we call it

struct BaseCallback
{
 virtual ~BaseCallback();
 virtual call();
};

We also write a single function that we always use for this C API:

void OurCallback( void * var )
{
   BaseCallback * ourBase = static_cast< BaseCallback * >)(var);
   ourBase->call();
}

As we are going to be casting always from void* to BaseCallback * we must be careful when we first supply the parameters the other way that we are always going to cast the BaseCallback* to void*.


I think, you're not using casting properly. I would recommend you to read first two responses from here:

When should static_cast, dynamic_cast, const_cast and reinterpret_cast be used?

Let's first know when which cast should be used!


EDIT:

As for your question,

is this code guaranteed to always work as in the code comments or is it UB? AFAIK it should be ok, but I'd like to be reassured.

If you want to pass pointer of ANY type to your to_A() function, then I think static_cast is not the one you should use. You should use reinterpret_cast. But if you want to pass only pointer to B ( or any derived class of A) to to_A() function, then it would always work. If by "work" you meant whether it will return non-null value.

Similar argument for to_B() function. I think, passing A* to to_B() might fail. That means, it could simply return NULL, Or if not null, A::f() will be called from to_B().

See this interesting output: http://www.ideone.com/2FZzw (it prints A::f(), not B::f() !!!)

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜