Search code examples
c++language-lawyerc++17new-operatormemory-alignment

Order between __STDCPP_DEFAULT_NEW_ALIGNMENT__ and alignof(std::max_align_t)


With GCC and Clang on x86-64/Linux alignof(std::max_align_t) and __STDCPP_DEFAULT_NEW_ALIGNMENT__ are both equal to 16.

With MSVC on x86-64/Windows alignof(std::max_align_t) is 8 and __STDCPP_DEFAULT_NEW_ALIGNMENT__ is 16.

The standard defines the two terms corresponding to these quantities in [basic.align]/3:

An extended alignment is represented by an alignment greater than alignof(std​::​max_­align_­t). [...] A type having an extended alignment requirement is an over-aligned type. [...] A new-extended alignment is represented by an alignment greater than _­_­STDCPP_­DEFAULT_­NEW_­ALIGNMENT_­_­.

I don't see that this implies any ordering between the two values, except if I interpret the term "new-extended" to imply "extended" from the spelling.

Is a conforming implementation of the C++ standard allowed to have

alignof(std::max_align_t) > __STDCPP_DEFAULT_NEW_ALIGNMENT__

?

And if it does, does this imply that object creation via

auto x = ::new(::operator new(sizeof(T))) T;

could be undefined behavior for some non-over-aligned type T?


Solution

  • If I read the standard correct, the new-extended alignment refers to all variants of void* operator new( std::size_t count, std::align_val_t al); (Those with std::align_val_t).

    So assuming the __STDCPP_DEFAULT_NEW_ALIGNMENT__ of 8 and std::max_align_t of 16, the allocation of long double will have to call ::operator new(16, std::align_val_t(16));. Something your compiler will have to do for you without you noticing.

    In practice, I believe that there are linux implementations out there that guarantee a new alignment of 8. (moz)jemalloc is one of them, for which this github-issue seems to confirm that the minimum alignment is 8 instead of 16. (I didn't find official documentation about it)

    If you want to use such implementation, you have to update the __STDCPP_DEFAULT_NEW_ALIGNMENT__ constant, for more details, see one of my questions: Overloading operator new with smaller default alignment.

    To answer your last question, I read auto x = ::new(::operator new(sizeof(T))) T; as explicitly calling the operator new instead of simply doing new T, in which case, I would assume you indeed have UB if T requires an alignment bigger than the default new alignment. Please note that this also holds if both constants are equal, as you can add alignas to the class to change alignment.

    Using these kind of classes requires extra care, as using custom allocators when using with std::vector.