Type casting

J

Jacob Jensen

This question has probably been asked a million time, but here it comes
again. I want to learn the difference between the three type cast operators:
static_cast, reinterpret_cast, dynamic_cast. A good way to do this is by
example. So I will give an example and please tell me what you think:

I have a base
class A
with a virtual destructor, and a
class B
that is it inherits publicly from A and defines som extra stuff.

If I have the following variables:

A* pA;
B* pB1 = new B();
B* pB2;

and the following assignment
pA = pB1;

which is most correct of the following
pB2 = static_cast< B* > (pA);
pB2 = reinterpret_cast< B* > (pA);
pB2 = dynamic_cast< B* > (pA);

The last one works only if I have RTTI set.
Why should I use one of them isntead of the other? Please could you explain
this to me as if I were a 6 year old? I have an idea about it, but I am
finding it difficult to understand that idea, if you know what I mean.

And thank you very much

Jacob
 
A

Alf P. Steinbach

* "Jacob Jensen said:
I have a base
class A
with a virtual destructor, and a
class B
that is it inherits publicly from A and defines som extra stuff.

If I have the following variables:

A* pA;
B* pB1 = new B();
B* pB2;

and the following assignment
pA = pB1;

which is most correct of the following
pB2 = static_cast< B* > (pA); // 1
pB2 = reinterpret_cast< B* > (pA); // 2
pB2 = dynamic_cast< B* > (pA); // 3

reinterpret_cast is just a way of telling the compiler to regard the bits
as having some other type than the original, and that's dangerous. The bits
may not even be meaningful when regarded as a value of the new type. So you
have essentially no well-defined operations on the result, other than casting
it back to the original type.

In this context a reinterpret_cast is meaningless and incorrect and dangerous,
although it might seem to "work" with a given compiler.

static_cast says to the compiler: change the type appropriately if there is
a well-defined conversion to the new type -- just do it, I know it's safe.

In this situation static_cast is appropriate because _you_ know it's safe.

dynamic_cast is like a static_cast except you're saying: "I'm not quite sure
whether this is safe, could you please do a runtime type check at this point".

Since you do know that the cast is safe a dynamic_cast would be inappropriate,
but not right out incorrect, here.

Summing up, _in this situation_ the most appropriate choice from the three you
have listed is static_cast.



(Comment: a downcast can often be avoided by introducing a virtual method.)


The last one works only if I have RTTI set.

No, it works with any conforming C++ compiler.

However, some C++ compilers, e.g. Microsoft's Visual C++, can be configured to
act like non-C++ compilers.

And in fact, Microsoft's Visual C++ compiler does not act like a C++ compiler
by default; it must be configured to act like one (options /GX and /GR to support
exceptions and RTTI, plus possibly esoteric linker option to support standard
'main').
 
J

John Harrison

dynamic_cast is like a static_cast except you're saying: "I'm not quite sure
whether this is safe, could you please do a runtime type check at this point".

Since you do know that the cast is safe a dynamic_cast would be inappropriate,
but not right out incorrect, here.

Just a minor point to add, which the OP may not know.

The way that dynamic_cast says that a cast is unsafe is by returning 0 (or
NULL) in the case of pointers and by throwing an exception (std::bad_cast I
think) in the case of references.

john
 
J

Jacob Jensen

reinterpret_cast is just a way of telling the compiler to regard the bits
as having some other type than the original, and that's dangerous. The bits
may not even be meaningful when regarded as a value of the new type. So you
have essentially no well-defined operations on the result, other than casting
it back to the original type.

Is it because a pointer to a type A may have a different byte representation
that a pointer to a type B?
But as you say, I will have "no well-defined operations on the result, other
than casting it back to the original type".
But is that not what I am doing? I am casting it back to the type it
originally was/is.
So if that is correct, then please tell me why I should not use it instead
of using static_cast (Note: Only tell me if my statement of casting it to
its original type is the correct)?
And when do I use reinterpret_cast then?

dynamic_cast is like a static_cast except you're saying: "I'm not quite sure
whether this is safe, could you please do a runtime type check at this
point".

Do I understand you correctly concerning the dynamic_cast thingy: There are
some cases when I do not know what type of pointer I have. And in that case,
I can try to cast it, and see if it gives a NULL pointer. That is my code
MUST handle the case where the cast is not valid. Am I right?
(Comment: a downcast can often be avoided by introducing a virtual
method.)

I would be interested in seeing a simple example of this. I need the above
downcast, because I have some API functions which I have to use that only
take the base class pointer as argument. So I have to upcast my pointer to
call the function ( func1() ), and then downcast it in a function that is
called by func1(), which is a function I have defined.
No, it works with any conforming C++ compiler.

However, some C++ compilers, e.g. Microsoft's Visual C++, can be configured to
act like non-C++ compilers.

Do you mean that all standard C++ compilers should have RTTI set?

Thank you again for your time. I really appreciate it.
 
A

Alf P. Steinbach

* "Jacob Jensen said:
* Alf said:
reinterpret_cast is just a way of telling the compiler to regard the bits
as having some other type than the original, and that's dangerous. The
bits may not even be meaningful when regarded as a value of the new type.
So you have essentially no well-defined operations on the result, other than
casting it back to the original type.

Is it because a pointer to a type A may have a different byte [you mean bit]
representation that a pointer to a type B?

It may in general, yes.

The standard lays out the rules in §5.2.10, with slightly different details
for different pointer types (pointer to function, to object, to member function),
but in general the only well-defined operation is to reinterpret_cast back to
the original type, in which case you get the original value back.

Exception: reinterpret_cast of null-pointer yields a valid null-pointer.

In your specific example I can't see any way the bits could be different,
but the standard is conservative: you can't use the result of a
reinterpret_cast for anything other than casting it back -- unless you
use knowledge of a particular compiler and system.

An example where it's obvious that different bit-level values must be
involved is when you have

class A{ ... };
class Middle1: public A { ... };
class Middle2: public A { ... };
class B: public Middle1, public Middle2 { ... };

Here a B object contains two different A subobjects. Casting using
static_cast from B* to Middle1* to A* gets you a pointer to one of them,
casting using static_cast from B* to Middle2* to A* gets you a pointer to
the other, and these two pointers can't very well be the same bit pattern,
for if they were then they would refer to the same A subobject.

In above paragraph I use 'casting' and 'static_cast' even though no actual
cast notation need be involved, since it's pure upcasting. But without
cast notation what you get _is_ a static_cast. It goes like:



#include <iostream>

struct A{ int ahum; };
struct Middle1: A { int m1; };
struct Middle2: A { int m2; };
struct B: Middle1, Middle2 { int bah; };

int main()
{
B object;
B* b = &object;
Middle1* m1 = b;
Middle2* m2 = b;
A* a1 = m1;
A* a2 = m2;

std::cout
<< a1 << "\n" // Some pointer value.
<< a2 << "\n" // Some different pointer value.
<< std::flush;

// Try to go directly from an A subobject to the B object.
// Not accepted, the compiler doesn't know _which_ A subobject.
// Not knowing where the pointer points it can't adjust it.
//
// B* bummer = static_cast<B*>( a1 ); // Nope.

// Go via the relevant subobject that _you_ know.
B* better = static_cast<B*>( static_cast<Middle2*>( a2 ) );

std::cout
<< b << "\n" // Some pointer value.
<< better << "\n" // The same pointer value.
<< std::flush;
}


But as you say, I will have "no well-defined operations on the result, other
than casting it back to the original type".
But is that not what I am doing? I am casting it back to the type it
originally was/is.

In your particular case: yes in practice no in standard. In general just no.

The reason is that the innocent upcasts are static_cast's, which in general
may change the pointer value (bitpattern).

A bitpattern-preserving (and not even that much is guaranteed!) reinterpret_cast
is then not guaranteed to get back the original value.


So if that is correct, then please tell me why I should not use it instead
of using static_cast (Note: Only tell me if my statement of casting it to
its original type is the correct)?
OK.


And when do I use reinterpret_cast then?

Only in very low-level, platform-specific code.



Do I understand you correctly concerning the dynamic_cast thingy: There are
some cases when I do not know what type of pointer I have. And in that case,
I can try to cast it, and see if it gives a NULL pointer. That is my code
MUST handle the case where the cast is not valid. Am I right?

Yep. Except as John Harrison noted, if you're casting to reference instead
of to pointer what you get as ungood-signal is an exception, not NULL. With
casting to pointer you get NULL.

method.)

I would be interested in seeing a simple example of this. I need the above
downcast, because I have some API functions which I have to use that only
take the base class pointer as argument. So I have to upcast my pointer to
call the function ( func1() ), and then downcast it in a function that is
called by func1(), which is a function I have defined.

Off the cuff,

struct A{ virtual void something(){} };
struct B{ virtual void something(){ ... B stuff ... } };

void calledFunc( A* pObj ){ pObj->something(); } // Does B stuff.

void func1( A* pObj ){ calledFunc( pObj ); }

int main()
{
B obj;
func1( &obj );
}

(Actually the best explanation I know of for virtual functions is as a
solution to avoid downcasts in e.g. an arithmetic expression evaluator.
But that's a digression. It's a shame that example isn't wider known.)

If the above doesn't seem possible there are more advanced versions of the
same basic idea -- look up the "visitor pattern".

And if that doesn't seem possible perhaps some redesign can do the trick.

In general you should never need downcasts except when taking objects out
of polymorphic containers (that is, containers containing a bunch of
objects of different but related types).



Do you mean that all standard C++ compilers should have RTTI set?

Yes.

All C++ compilers must support RTTI and compile RTTI-based code
correctly, since RTTI is part of the language defined by the standard.

Without RTTI (or exceptions, or 'main' ;-) ) you have only a subset of C++.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,164
Messages
2,570,901
Members
47,439
Latest member
elif2sghost

Latest Threads

Top