Search code examples
c++castingreinterpret-caststrict-aliasingstatic-cast

Why can I use static_cast With void* but not With char*


I know that reinterpret_cast is primarily used going to or from a char*.

But I was surprised to find that static_cast could do the same with a void*. For example:

auto foo "hello world"s;
auto temp = static_cast<void*>(&foo);
auto bar = static_cast<string*>(temp);

What do we gain from using reinterpret_cast and char* over static_cast and void*? Is it something to do with the strict aliasing problem?


Solution

  • Your question really has 2 parts:

    1. Should I use static_cast or reinterpret_cast to work with a pointer to the underlying bit pattern of an object without concern for the object type?
    2. If I should use reinterpret_cast is a void* or a char* preferable to address this underlying bit pattern?

    static_cast: Converts between types using a combination of implicit and user-defined conversions

    In 5.2.9[expr.static.cast]13 the standard, in fact, gives the example:

    T* p1 = new T;
    const T* p2 = static_cast<const T*>(static_cast<void*>(p1));
    

    It leverages the implicit cast:

    A prvalue pointer to any (optionally cv-qualified) object type T can be converted to a prvalue pointer to (identically cv-qualified) void. The resulting pointer represents the same location in memory as the original pointer value. If the original pointer is a null pointer value, the result is a null pointer value of the destination type.*

    There is however no implicit cast from a pointer of type T to a char*. So the only way to accomplish that cast is with a reinterpret_cast.

    reinterpret_cast: Converts between types by reinterpreting the underlying bit pattern

    So in answer to part 1 of your question when you cast to a void* or a char* you are looking to work with the underlying bit pattern, reinterpret_cast should be used because it's use denotes to the reader a conversion to/from the underlying bit pattern.

    Next let's compare void* to char*. The decision between these two may be a bit more application dependent. If you are going to use a standard library function with your underlying bit pattern just use the type that function accepts:

    • void* is used in the mem functions provided in the cstring library
    • read and write use char* as inputs

    It's notable that C++ specific libraries prefer char* for pointing to memory. Holding onto memory as a void* seems to have been preserved for compatibility reasons as pointer out here. So if a cstring library function won't be used on your underlying bit patern, use the C++ specific libraries behavior to answer part 2 of your question: Prefer char* to void*.