6

I'm using std::aligned_storage as the backing storage for a variant template. The problem is, once I enable -O2 on gcc I start getting warnings of 'dereferencing type-punned pointer will break strict aliasing`.

The real template is much more complex (type checked at runtime), but a minimal example to generate the warning is:

struct foo
{
  std::aligned_storage<1024> data;

  // ... set() uses placement new, stores type information etc ...

  template <class T>
  T& get()
  {
    return reinterpret_cast<T&>(data); // warning: breaks strict aliasing rules
  }
};

I'm pretty sure boost::variant is doing essentially the same thing as this, but I can't seem to find how they avoid this issue.

My questions are:

  • If using aligned_storage in this way violates strict-aliasing, how should I be using it?
  • Is there actually a strict-aliasing problem in get() given that there are no other pointer based operations in the function?
    • What about if get() is inlined?
    • What about get() = 4; get() = 3.2? Could that sequence be reordered due to int and float being different types?
marack
  • 2,024
  • 22
  • 31
  • does the alignment affect the aliasing problem in any way?! expect it is part of the reason (but this doesn't change at all the point that the standard fobids it, doens't it?) – dhein Oct 07 '13 at 16:45
  • @Zaibis No, using `aligned_storage` as opposed to any other type as a buffer shouldn't make any difference. The reason I've specified `aligned_storage` here is that it seems to have been designed with this sort of use in mind. – marack Oct 07 '13 at 21:09
  • @marack: you're not supposed to (and really not allowed to) use `aligned_storage` directly. I edited my answer to try to make that (even) more clear. – rici Oct 07 '13 at 22:27
  • I just were more asking about the alignmend to aliasing, as ou tagged this as strict-aliasing and i don't get what it has to do with it. – dhein Oct 08 '13 at 07:02

1 Answers1

5

std::aligned_storage is part of <type_traits>; like most of the rest of the inhabitants of that header file, it is just a holder for some typedefs and is not meant to be used as a datatype. Its job is to take a size and alignment, and make you a POD type with those characteristics.

You cannot use std::aligned_storage<Len, Align> directly. You must use std::aligned_storage<Len, Align>::type, the transformed type, which is "a POD type suitable for for use as uninitialized storage for any object whose size is at most Len and whose alignment is a divisor of Align." (Align defaults to the largest useful alignment greater than or equal to Len.)

As the C++ standard notes, normally the type returned by std::aligned_storage will be an array (of the specified size) of unsigned char with an alignment specifier. That avoids the "no strict aliasing" rule because a character type may alias any other type.

So you might do something like:

template<typename T>
using raw_memory = typename std::aligned_storage<sizeof(T),
                                                 std::alignment_of<T>::value>::type;

template<typename T>
void* allocate() { return static_cast<void*>(new raw_memory<T>); }

template<typename T, typename ...Arg>
T* maker(Arg&&...arg) {
   return new(allocate<T>()) T(std::forward<Arg>(arg)...);
}
rici
  • 234,347
  • 28
  • 237
  • 341
  • Ahh thanks. I didn't realize that `aligned_storage` was a trait exposing a type rather than the type itself. As you mentioned, using `aligned_storage<...>::type` resolves the strict aliasing issue correctly. – marack Oct 07 '13 at 23:22
  • 7
    This case is *other type* aliasing a character type. The strict aliasing rule is not symmetrical, it outlaws *other type* aliasing character type but it permits character type aliasing *other type* (which is not happening here). However, [this answer](http://stackoverflow.com/questions/13466556/aligned-storage-and-strict-aliasing) argues that `aligned_storage` does not violate strict aliasing for other reasons (although I don't quite follow its argument). – M.M Mar 12 '15 at 03:24