
C++ type casting with example for C developers - IndianWestCoast
http://www.vishalchovatiya.com/cpp-type-casting-with-example-for-c-developers/
======
CoolGuySteve
I can't think of a dynamic_cast scenario I've seen that was not indicative if
an improper class design; typically that the base class doesn't have proper
virtual methods defined, or there's a missing ABC interface, or the
inheritance is just inappropriate.

It's also brutally slow in most compilers.

After that consideration, C-casts are mostly fine.
static_cast/reinterpret_cast are more explicit but the syntax is bizarrely
verbose.

But I'm way more annoyed by dynamic_cast than C-casts whenever it they come
up.

~~~
MauranKilom
What do you think of the following:

    
    
      class AInterface { /*...*/ };
      class BInterface : public AInterface { /*...*/ };
      class CInterface : public BInterface { /*...*/ };
    
      class AImpl : public AInterface { /*...*/ };
      class BImpl : public BInterface { /*...*/ };
      class CImpl : public CInterface { /*...*/ };
    

In this use case, assume going from AInterface to CInterface implies stronger
and stronger guarantees that the implementations provide. Also assume LSP as
satisfied.

Now, you are given an AInterface* object and want to know whether it has a
certain guarantee or not. A dynamic cast answers this with minimal space
overhead.

Yes, there is certainly run time overhead in the dynamic cast, and you could
just have a virtual method that returns some encoding of the guarantees (e.g.
as an enum), but the dynamic_cast is still pareto-optimal in terms of space.

Change my mind?

~~~
CoolGuySteve
I would argue that much like Go's aversion to deep inheritance hierarchies,
this interface hierarchy would be annoying to use in practice.

If A, B, and C are different families of functionality then ditch the
inheritance and use separate interfaces for each family of functionality.

The cascading dynamic_cast logic required is a code smell that this could be
made simpler.

~~~
cryptonector
> this interface hierarchy would be annoying to use in practice

No doubt, but it happens, and sometimes because the refactoring you might
prefer to do can't be done or can't be done right away.

For example, in Java you might have a subclass of Principal where you store
additional metadata and have additional methods, then you might have code
like:

    
    
      public static Something princOk(Principal p, Stuff stuff)
        throws MyPrincipalException
      {
        if (p instanceof MyPrincipal)
          return princOk((MyPrincipal)p, stuff);
        return princOk(MyPrincipal(p), stuff);
      }
    

Why would you do this? Well... because many JDK and third-party interfaces
take Principal object arguments that you could reuse... if only you could
decorate those objects with the additional metadata that you wanted, and...
with subclassing you get to.

In Java you might say "use Subject", but it's not that easy, and anyways, JAAS
is a terrible terrible thing that should have been ripped out when applets
were ripped out.

~~~
CoolGuySteve
In C++, you can use templates to pattern match/duck type interfaces at compile
time.

So "template<typename PrincType> void princOk(PrincType& p, Stuff& stuff)
{p.doThing(stuff); }" would map properly as long as doThing was implemented in
all the different classes. You don't actually need an interface or base class
for it to work.

The rest of the dynamic mapping is faster by calling switch() on an enum class
member and than static_cast rather than relying on dynamic_cast.

------
kccqzy
There's another, not in the C++ language but in the C++ standard library,
called bit_cast:
[https://en.cppreference.com/w/cpp/numeric/bit_cast](https://en.cppreference.com/w/cpp/numeric/bit_cast)

It essentially just calls memcpy after some basic checking. It's similar to
reinterpret_cast in spirit but more powerful. You can convert for example a
float array into a char array (I don't think any of the four standard casts
will allow you to convert a whole array at a time). Compilers like clang will
generally recognize memcpy as __builtin_memcpy which sometimes generates no
instructions at all.

~~~
dirtydroog
I suggested an underlying_cast function that converts a typed-enum to its
underlying type. It's quite useful for things like sending typed enums to
streams.

~~~
saagarjha
That's just static_cast<std::underlying_type_t<EnumType>>(value), right?

------
keldaris
I'm sure this will be controversial, but my attitude towards C++ type casting
is much, much simpler.

1\. I only use static_cast, the other three C++-specific casts are, in my
opinion, somewhere between useless and harmful.

2\. The only meaningful reason to prefer static_cast over C-style casts is
easy greppability. If you find you don't benefit from this difference, C-style
casts are just fine.

These are obviously highly opinionated personal rules that serve me well in
the subset of C++ I prefer to use. Your mileage can and very likely will vary
substantially.

~~~
kllrnohj
Now that `mutable` exists const_cast should indeed be avoided.

But `reinterpret_cast` has quite a bit of use, particularly in FFI scenarios.
For example when using JNI there is no void* type in Java, so you are forced
to store the pointer in a jlong. But you also need to be super duper sure that
no conversion happens, it's _just_ a really tiny 8-byte allocation. So you
reinterpret_cast back & forth. My JNI code is littered with variants of
reinterpret_cast<jlong>( T* ) & reinterpret_cast< T* >(jlong) - and it is
neither useless nor harmful.

reinterpret_cast is also quite useful when writing custom allocators. More
niche usage there, of course, but still far from useless or harmful.

> 2\. The only meaningful reason to prefer static_cast over C-style casts is
> easy greppability. If you find you don't benefit from this difference,
> C-style casts are just fine.

If you do actually think that static_cast is the only useful cast then this is
super wrong advice. C-style casts are "compiler guesses if it's static_cast or
reinterpret_cast, and const-ness is completely ignored." They are not a
shortened alias for static_cast.

~~~
JoeAltmaier
Agreed. But triggered by a thing written there: a pointer in a jlong? Is there
any guarantee these things are the same size? Works on one platform, cool,
maybe that's enough. But doesn't sound portable.

~~~
klodolph
jlong is always 64 bits.

I would be interested in knowing more details about platforms where your
pointers are wider than 64 bits. Not impossible, but certainly pathological.

~~~
JoeAltmaier
Complex pointers (object/method pointers) can sometimes exceed that?

Anyway, I see the issue, you're stuck having to do something and that's a
pretty good solution. Maybe add an assert somewhere to the effect that
sizeof(jlong) >= sizeof(pointer) which is free on your platform.

~~~
kllrnohj
> Complex pointers (object/method pointers) can sometimes exceed that?

They can't. void* / intptr_t is a fixed-size, and at least on all platforms I
run on that size is always 32-bit or 64-bit. Both of which fit perfectly fine
in the always-64-bit jlong.

If you're thinking of std::mem_fn or similar note that they are defined to
return an undefined _object_ not a pointer. So of course those can be larger
than the size of a pointer, since they aren't a pointer.

But sure toss in a static_assert(sizeof(jlong) >= sizeof(void*)) if you want.

~~~
JoeAltmaier
I guess I was thinking of virtual function pointer. Its larger than a regular
function pointer, which is not necessarily the same size as a data pointer.
But if you're dealing with object pointers only, all good!

------
aaaronic
This article spends a ton of time explaining very rudimentary things and then
completely glosses over what it intends to convey.

C-style casts aren't evil, but they are powerful. They can also be used in
places where more than one C++ cast would be required. C developers are very
likely to use the C style because it mostly gets the job done the first try.

C developers working on C++ code are not likely to be swayed much by anything
that doesn't make a strong case for having 4 different cast operators and this
article doesn't manage to do that.

~~~
RcouF1uZ4gsC
I agree, the article tells how but not why.

I think there are two main reasons to prefer the C++ style casts.

1\. The C style cast does too much. Some of the conversion it does are pretty
safe, some are dangerous. The C++ casts try to differentiate the relatively
safe casts from the dangerous casts. Any code with reinterpret_cast is worth
taking extra time to scrutinize.

2\. The second is that C style casts are hard to search for (Type). By giving
names to these operations, you can more quickly search code for problematic
areas.

3\. Incorrect aliasing is a big deal. In the C and C++ standards it is
undefined behavior, which means you lose the ability to reason about your
program. Making the operations that can trigger this stand out is a good
thing.

------
munk-a
I default to blacklisting all js and I feel like this website is a great
example of an utterly unnecessary usage of front end scripting and the no-js
experience is... well, just a blank screen with a wobbly needle.

~~~
hawski
It has a jarring and useless full-screen white curtain up animation.

------
kccqzy
This might be off topic, and certainly unimportant, but I never liked
underscores in language keywords. For example if your C++ style guide calls
for camelCase identifiers these look jarring. Any reason why the designer
didn't just introduce three keywords "cast" "dynamic" "reinterpret" instead?
Note that "static" and "const" are already keywords.

------
drenvuk
I use c style casts everywhere and I've stopped using every other style. Why?
Because my projects are my own and I know what everything does and what
everything should be. I used the other style in a couple c++ based projects
and then never again. Wouldn't touch them unless a team made me.

~~~
stinos
_I know what everything does and what everything should be_

I used to be like that, but turns out after 5 years not looking at a piece of
code you don't know anything anymore (well at least I don't) and then change
something only to have something else blow up because, hey, C-style casts ftw
right, less typing and less caring.

The other casts at least offer some protection against that at compile-time.
There's already enough loopholes in C++ that I'd rather have the least worries
possible so now I just do the right thing and use the right cast. Heck I might
even add a bunch of static_asserts for casts of which I know they'll only be
ok if both types are the same size and layout for instance. Plus at the same
time they serve as documentation of what I meant when writing he cast. Saved
me a bunch of times already.

