What is type punning?
Type punning is like poking holes - a technique to subvert the language’s type system. There are recommended ways to perform type punning and others that you should actively seek to avoid.
A very common task is the following : you have an object of one type - let’s say a float and then you want to pretend that the bytes of this object are actually an int or vice versa.
+--+--+--+--+
float |F0|00|80|01|
+--+--+--+--+
+--+--+--+--+
int |F0|00|80|01|
+--+--+--+--+
Another class of type punning is the following. You have an object and you either want to look at the raw bytes of the object or the other way round, you just have a blob of bytes and you want to pretend, that they are an object of some type. Whereas in reality, it’s just a bunch of bytes.
+--+--+--+--+
float |F0|00|80|01|
+--+--+--+--+
+--+ +--+ +--+ +--+
char* |F0| |00| |80| |01|
std::byte* +--+ +--+ +--+ +--+
C-style casts
Suppose we have \(2\) distinct types - we have a Widget and we have a Gizmo, those are different types. There is no inheritance going on here. Each one of them has a function that prints something. We are creating a Gizmo and we are casting it to a Widget and calling its .doSomething() method.
#include <print>
struct Widget{
virtual void doSomething(){
std::println("Widget");
}
};
struct Gizmo{
virtual void doSomethingCompletelyDifferent(){
std::println("Gizmo");
}
}
int main(){
Gizmo gizmo;
Widget* widget = (Widget*)(&gizmo); // [1]
widget->doSomething();
}[1] compiles - this is a C-style cast. If you look at the standard, what a C-style cast does, it says that, well it says that a C-style cast is going to try all these things : first it’s going to try const_cast, then it’s going to try static_cast, and if all these things fail, then it will try a reinterpret_cast. If that succeeds, then, that’s what you get. So, the above code is going to compile and run. The question is what is it going to do?
This is undefined behavior(UB). You can’t just cast Gizmo to Widget. If you run this code snippet on clang with optimizations on, it prints Gizmo. If you try it on gcc, the compiler detects that this is a gizmo, you cannot reach this line of code, this cannot happen and then it optimizes away everything before it that lead to it, which is basically the whole program - so it doesn’t produce any output.