c++language-lawyerstrict-aliasing

Standard compliant way of swapping bits of two objects in different types


Consider the following two functions:

template<typename T, typename S>
void swap1(T* t, S* s)
{
  static_assert(sizeof(T) == sizeof(S));
  char tmp[sizeof(T)];
  std::memcpy(tmp, t, sizeof(T));
  std::memcpy(t,  s, sizeof(T));
  std::memcpy(s, tmp, sizeof(T));
}

and

template<typename T, typename S>
void swap2(T* t, S* s)
{
  static_assert(sizeof(T) == sizeof(S));
  char *tc = (char*)t, *sc = (char*)s;
  std::swap_ranges(tc, tc + sizeof(T), sc);
}

Which of them violates the strict aliasing rule? Do both of them violate the rule? If both, how to achieve the goal without violation?

All I can see from C++ standard is that, we can use pointers of certain types including char* to point at an arbitrary object, and access its stored value. What does access mean? Is it just read or read+write?

I didn't find an emphatic answer from What is the Strict Aliasing Rule and Why do we care?. Discussion on Hacker News - memcpy certainly violates aliasing rules confused me even more.

But by reading the "memcpy version, compliant to C and C++ specs and efficient" section of Understanding C/C++ Strict Aliasing, it seems at least swap2 is perfectly fine.


Solution

  • Neither implementation violates the strict aliasing rule. The rule prohibits reading or writing T* via U* unless U* is char*/unsigned char*/std::byte*. Reading and writing through char* is perfectly standard C++.

    Whether the object ends up having some value in the end is a whole another story. I don't think standard guarantees anything unless T and U are the same type. Maybe barring some arithmetic conversions. But I'm not sure.

    If you only have a single trivially copyable type T, [basic.types]/2 says (omitting some details):

    For any object ... of trivially copyable type T ... the underlying bytes ... making up the object can be copied into an array of char ... If the content of that array is copied back into the object, the object shall subsequently hold its original value.