1

Given the following code, is proposition 2 equivalent to proposition 4? I can't define "equivalent" precisely though.

int main() {
    // Keep your mind of of (void) its just here to remove unused expression
    // result warnings

    using T = int;
    using U = char;
    using V = unsigned long;

    // Proposition 1
    // As far as I know, only cstyle and reinterpret_cast can to that
    (void)reinterpret_cast<V>(nullptr);
    (void)(V)(nullptr);

    // Proposition 2
    // This snipet wont compile, as expected
    // (void)static_cast<V>(nullptr);

    // Proposition
    // Casting U* to T* using reinterpret_cast OK
    (void)reinterpret_cast<T *>(static_cast<U *>(nullptr));

    // Proposition 3
    // Casting U* to T* using static_cast, wont compile as expected
    // (void)static_cast<T *>(static_cast<U *>(nullptr));

    // Proposition 4
    // Casting U* to void* is always OK, then casting from void* to T* is always
    // possible using static_cast
    (void)static_cast<T *>(static_cast<void *>(static_cast<U *>(nullptr)));
}

The code is also available here: https://godbolt.org/z/1xbvsWrhz

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
Etienne M
  • 604
  • 3
  • 11
  • Sure, you can do whatever you want by casting through `void*`, but it won't work at runtime. If I have a variable `a` of type `A`, then I can get to any type `B` via `*(B*)(void*)(&a)`. The type checker will take that, but it's almost certainly undefined behavior. – Silvio Mayolo Oct 02 '21 at 16:41
  • I found https://stackoverflow.com/questions/332030/when-should-static-cast-dynamic-cast-const-cast-and-reinterpret-cast-be-used?rq=1 very helpful for understanding the various cast types. – Mike Lischke Oct 02 '21 at 16:43
  • These cases listed separately on [cppereference](https://en.cppreference.com/w/cpp/language/reinterpret_cast): `4) Any value of type std::nullptr_t, including nullptr can be converted to any integral type as if it were (void*)0`, `5) Any object pointer type T1* can be converted to another object pointer type cv T2*. This is exactly equivalent to static_cast(static_cast(expression))... the resulting pointer may only be dereferenced safely if allowed by the type aliasing rules (see below)`. So, it looks they are not equivalent and probably also defined this way in the standard. – dewaffled Oct 02 '21 at 16:51
  • It would be more interesting if the types were related, preferably through multiple inheritance so that the static cast actually does something. For a null pointer even a static cast -- that would change the offset were it non-null -- preserves the null pointer. Anything else would be surprising. Without digging through the standard I bet you that any cast of a null pointer (not only the `nullptr` constant) preserves the null pointer. – Peter - Reinstate Monica Oct 02 '21 at 17:25
  • @SilvioMayolo Not sure whether the cast is UB -- accessing it will be because of aliasing. – Peter - Reinstate Monica Oct 02 '21 at 17:28
  • @Peter-ReinstateMonica The expression in question is `*(B*)(void*)(&a)`. The leading `*` is a dereference - that's what'll likely cause UB. – Igor Tandetnik Oct 02 '21 at 23:28
  • @IgorTandetnik Oh, true. Missed the dereference. – Peter - Reinstate Monica Oct 03 '21 at 06:51

0 Answers0