Do std::optional and boost::optional respect alignment restrictions of the managed object?

998 views Asked by At

If a class T has an alignment requirement, such as one specified by the alignas keyword, are std::optional<T> and boost::optional<T> guaranteed to respect said alignment?

If they were simply wrapper classes for a T object and a bool initialized, then they would automatically align their T member as required, but the standard and boost documentation state that they can hold no object and deal well with expensive to construct objects. From this I understand that they don't simply contain a T. Rather, they seem to allocate a buffer upon which the T is constructed or destroyed manually. Therefore, the C++ language will not automatically align the buffer because it is not of type T.

So, do std::optional<T> and boost::optional<T> properly align their managed T object? Do they also provide optional<T>::operator new and optional<T>::operator new[] that respect the alignment requirement?

2

There are 2 answers

2
PaulR On BEST ANSWER

A conforming implementation of std::optional must respect the alignment requirements of its value_type according to the C++17 standard draft:

23.6.3 Class template optional [optional.optional]

(...) The contained value shall be allocated in a region of the optional storage suitably aligned for the type T.

An implementor could use a union potentially containing the value as member, ensuring proper alignment.

As Thomas already mentioned while I was looking it up, boost::optional does ensure proper alignment by use of aligned_storage<T>

1
Thomas On

In the GNU C++ implementation, std::optional derives from _Optional_Base<_Tp>, which in turn contains an _Optional_payload<_Tp>. This is a template with several specializations, all of which contains the following members:

  using _Stored_type = remove_const_t<_Tp>;
  struct _Empty_byte { };
  union {
      _Empty_byte _M_empty;
      _Stored_type _M_payload;
  };
  bool _M_engaged = false;

As you can see, the _M_payload is actually strongly typed, so any alignment attributes should carry over properly in this implementation.


The Boost implementation does not use a union. Instead, boost::optional<T> derives from optional_base<T>, which simply contains:

 typedef aligned_storage<T> storage_type ;
 bool m_initialized ;
 storage_type m_storage ;

The name of aligned_storage is a clue; the docs don't mention that the second template argument is optional and defaults to -1, which means boost::detail::max_align, which means to use the alignment a union type containing a bunch of primitive types up to 128 bits in length. So (from a cursory reading) Boost seems to be a bit more pessimistic than GNU, but will also get your alignment right.