Search code examples
c++castingundefined-behavior

Strict aliasing rule: static_cast vs reinterpret_cast


Consider source type S and destination type D, which are unrelated types but have the same layout (for example S is a struct that simply wraps D, or vice versa, or S and D happen to have same data members in the same order, etc). Each of S and D can be either of primitive type or class type.

(A)

S* s;
D* d = reinterpret_cast<D*>(s);
//use d

(B)

S* s;
D* d = static_cast<D*>(static_cast<void*>(s));
//use d

I'm aware that we already have undefined behaviour when doing these casts, as per strict aliasing rule - although in practice it would most likely work.

Rules for strict aliasing is here. Unrelated means same layout but violate strict aliasing.

My question is:

Is (A) and (B) exactly equivalent? when

(1). S and D are unrelated, as stated above.

(2). S and D are related such that the casts do not violate strict aliasing rule, hence not UB.


Solution

  • In C++14 (expr.reinterpret.cast/7), if s is correctly aligned for D, then the definition of reinterpret_cast<D *>(s) is static_cast<D *>(static_cast<void *>(s)). So your two cases are exactly equivalent.

    If there is an alignment violation then the result of the cast is unspecified, meaning it's effectively undefined behaviour if the result is dereferenced.

    Note: The subsequent // use d may be undefined behaviour for various reasons; but if so, then it's either undefined in both cases, or the same well-defined behaviour in both. You mention "strict aliasing" in the title, however strict aliasing concerns only apply to *d , not to the cast itself.

    In C++11 it was required that S and D both be standard-layout types, otherwise the result is unspecified.

    In C++03, it was only defined that, if s is correctly aligned for D, then s == (S *)(D *)s. It was not specifically required that (D *)s be usable in any other way than casting back to S *. Your two cases may have differed in C++03. This was obviously an unsatisfactory specification.