C++ – How do variables in C++ store their type

cdata typesvariables

If I define a variable of a certain type (which, as far as I know, just allocates data for the content of the variable), how does it keep track of which type of variable it is?

Best Answer

Variables (or more generally: “objects” in the sense of C) do not store their type at runtime. As far as machine code is concerned, there is only untyped memory. Instead, the operations on this data interpret the data as a specific type (e.g. as a float or as a pointer). The types are only used by the compiler.

For example, we might have a struct or class struct Foo { int x; float y; }; and a variable Foo f {}. How can a field access auto result = f.y; be compiled? The compiler knows that f is an object of type Foo and knows the layout of Foo-objects. Depending on platform-specific details, this might be compiled as “Take the pointer to the start of f, add 4 bytes, then load 4 bytes and interpret this data as a float.” In many machine code instruction sets (incl. x86-64) there are different processor instructions for loading floats or ints.

One example where the C++ type system cannot keep track of the type for us is an union like union Bar { int as_int; float as_float; }. An union contains up to one object of various types. If we store an object in an union, this is the union's active type. We must only try to get that type back out of the union, anything else would be undefined behavior. Either we “know” while programming what the active type is, or we can create a tagged union where we store a type tag (usually an enum) separately. This is a common technique in C, but because we have to keep the union and the type tag in sync this is fairly error prone. A void* pointer is similar to an union but can only hold pointer objects, except function pointers.
C++ offers two better mechanisms to deal with objects of unknown types: We can use object-oriented techniques to perform type erasure (only interact with the object through virtual methods so that we don't need to know the actual type), or we can use std::variant, a kind of type-safe union.

There is one case where C++ does store the type of an object: if the class of the object has any virtual methods (a “polymorphic type”, aka. interface). The target of a virtual method call is unknown at compile time and is resolved at run time based on the dynamic type of the object (“dynamic dispatch”). Most compilers implement this by storing a virtual function table (“vtable”) at the start of the object. The vtable can also be used to get the type of the object at runtime. We can then draw a distinction between the compile-time known static type of an expression, and the dynamic type of an object at runtime.

C++ allows us to inspect the dynamic type of an object with the typeid() operator which gives us a std::type_info object. Either the compiler knows the type of the object at compile time, or the compiler has stored the necessary type information inside the object and can retrieve it at runtime.