19

As far as I understand, GCC supports all of its C99 features in C++. But how is C99 strict aliasing handled in C++ code?

I know that casting with C casts between unrelated types is not strict-aliasing-safe and may generate incorrect code, but what about C++? Since strict aliasing is not part of C++ standard (is that correct?), GCC must be specifying the semantics itself.

I figure const_cast and static_cast cast between related types, hence they are safe, while reinterpret_cast can break strict aliasing rules.

Is this a correct understanding?

Alex B
  • 82,554
  • 44
  • 203
  • 280

3 Answers3

34

No, you are probably mixing different things.

Strict aliasing rules have absolutely nothing to do with C99 standard specifically. Strict aliasing rules are rooted in parts of the standard that were present in C and C++ since the beginning of [standardized] times. The clause that prohibits accessing object of one type through a lvalue of another type is present in C89/90 (6.3) as well as in C++98 (3.10/15). That's what strict aliasing is all about, no more, no less. It is just that not all compilers wanted (or dared) to enforce it or rely on it. Both C and C++ languages are sometimes used as "high-level assembly" languages and strict aliasing rules often interfere with such uses. It was GCC that made that bold move and decided to start relying on strict aliasing rules in optimizations, often drawing complaints from those "assembly" types.

It is true that the most straightforward way to break strict aliasing rules in C++ is reinterpret_cast (and C-style cast, of course). However, static_cast can also be used for that purpose, since it allows one to break strict aliasing by using void * as an intermediate type in a "chained" cast

int *pi;
...
double *pd = static_cast<double *>(static_cast<void *>(pi));

const_cast cannot break strict aliasing in a compliant compiler.

As for C99... What C99 did introduce was the restrict qualifier. This is directly related to aliasing, but it is not what is known as strict aliasing per se.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • 1
    You are right, I was thinking C99 `restrict` keyword, rather than strict aliasing. For some reason it has ingrained in my mind this way ("C99" + "strict aliasing"). – Alex B May 05 '10 at 07:04
  • So what exactly is the clause in question? Does it mean that any C89/C99/C++98 code that breaks strict aliasing rules is technically incorrect (barring compiler-specific switches like -fno-strict-aliasing)? – Alex B May 05 '10 at 07:07
  • @Checkers: I added the numbers to my answer. And yes, the code that breaks strict aliasing rules exhibits undefined behavior, in C89 and in C++98 as well. – AnT stands with Russia May 05 '10 at 07:16
  • 4
    I'm still not sure whether it's possible to use the `sockaddr`-related parts of the BSD sockets API without breaking the strict aliasing rules. – caf May 05 '10 at 12:23
  • caf: Using unions is much safer, and I'm pretty sure it's fully defined behavior as well. – Maister Jan 03 '11 at 06:34
  • 4
    @Maister: no, using unions to cast between types is are undefined behavior. The standard only allows to read from the same variable of an union that was last written to. –  Feb 20 '11 at 19:56
  • 2
    @jons34yp - additionally, if there's an union of two structs, then the standard also allows to access the initial common field subsequence in both, IIRC. – Kos Aug 01 '11 at 13:03
  • 5
    @caf: I believe that the sockaddr-related parts of BSD sockets API (and any other API doing polymorphism in C) does not actually violate the strict aliasing, because you are allowed to access via "an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union)", so accessing the `sa_family_t`-typed first field via two differently-typed structure pointers seems allowed. – Jan Hudec May 16 '12 at 09:31
4

static_cast can break aliasing rules too, because the compiler is trusting you to ensure that the target type is related to the actual runtime type of the object. Consider:

extern void f(double*, int*); // compiler may optimize assuming that arguments don't overlap
double d;
void* pv = &d;
int* pi = static_cast<int*>(pv);
f(&d, pi); // assumption is violated
Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
2

The concept is the same in Cpp; in that you can use C style casts to guide you through what is considered safe wrt strict aliasing.

In short: no, the approach to using Cpp casting (that you've outlined) will not safely cover all cases. One common way to break the rules is to use static_cast to cast pointers.

Just turn up the compiler warnings -- it will (or, should) tell you what is unsafe.

justin
  • 104,054
  • 14
  • 179
  • 226