4

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?

walnut
  • 21,629
  • 4
  • 23
  • 59

1 Answers1

2

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.

JVApen
  • 11,008
  • 5
  • 31
  • 67
  • `-fnew-alignment` on Clang does indeed allow to produce the ordering, but it does not seem to affect the default `operator new` implementation. The question is whether this is standard-conform behavior or not. If it is conform, then generic library code such as `std::vector` should support it (because `alignof(std::max_align_t)`, not `__STDCPP_DEFAULT_NEW_ALIGNMENT__`, is the upper bound for alignments that are ["*supported by the implementation in all contexts*"](https://timsong-cpp.github.io/cppwp/n4659/basic.align#2). – walnut Dec 04 '19 at 11:26
  • If it is standard-conform then I should write library code using placement-new of generic types with that in mind since it should support all types with *fundamental alignment*. Types with explicitly extended alignment with `alignas` are special and I don't need to be generally supported, but basically the question is whether there are types which were not extended this way but for which the result of a call to `operator new(std::size)` is still not sufficiently aligned. – walnut Dec 04 '19 at 11:31
  • @walnut The `-fnew-alignment` should be added with a global overload of the ` operator new` Looking at the `std::vector` implementation of libc++, I see explicit call with alignment: https://github.com/llvm-mirror/libcxx/blob/78d6a7767ed57b50122a161b91f59f19c9bd0d19/include/memory#L1853 – JVApen Dec 04 '19 at 11:40
  • Your second remark is confusing me, however, if you use `std::allocator` or simply `new` instead of calling the operator directly, I don't see problems. If you bypass ` new` and call ::operator new, you'll need to do something special. (Or simply static assert that you don't support this setup yet) – JVApen Dec 04 '19 at 11:45
  • It surprises me that there is something different here in C++17. In C++14 and before, there is no `align_t` version of `operator new`, but the the other overload is always sufficient for non-over-aligned types. In C++17 however I may need to call the `align_t` version to make sure that it'll work with all non-over-aligned types, going by this answer. The `__libcpp_allocate` function in the libc++ code you linked also switches between the two depending on the `__cplusplus` macro. (The question is rather hypothetical. As you say usually direct calls to `operator new` are not needed.) – walnut Dec 04 '19 at 12:41
  • I actually think that before C++14, the allocation of over aligned classes was always UB, which is why [`aligned_alloc`](https://www.boost.org/doc/libs/1_71_0/doc/html/align/reference.html#align.reference.functions.aligned_alloc) exists – JVApen Dec 04 '19 at 12:47
  • Yes, sure. I was referring to non-over-aligned types, those with alignment at most `alignof(std::max_align_t)`, but potentially larger than `_­_­STDCPP_­DEFAULT_­NEW_­ALIGNMENT_­_­`. – walnut Dec 04 '19 at 12:50