basic.stc.dynamic.allocation/2 of N3337 (basically C++11):
The allocation function attempts to allocate the requested amount of
storage. If it is successful, it shall return the address of the start
of a block of storage whose length in bytes shall be at least as large
as the requested size. There are no constraints on the contents of the
allocated storage on return from the allocation function. The order,
contiguity, and initial value of storage allocated by successive calls
to an allocation function are unspecified. The pointer returned shall
be suitably aligned so that it can be converted to a pointer of any
complete object type with a fundamental alignment requirement (3.11)
and then used to access the object or array in the storage allocated
(until the storage is explicitly deallocated by a call to a
corresponding deallocation function). Even if the size of the space
requested is zero, the request can fail. If the request succeeds, the
value returned shall be a non-null pointer value (4.10) p0 different
from any previously returned value p1, unless that value p1 was
subsequently passed to an operator delete. The effect of dereferencing
a pointer returned as a request for zero size is undefined.
Fundamental alignment (basic.align/2):
A fundamental alignment is represented by an alignment less than or
equal to the greatest alignment supported by the implementation in
all contexts, which is equal to alignof(std::max_align_t)
Extended alignment (basic.align/3):
An extended alignment is represented by an alignment greater than
alignof(std::max_align_t).
It is implementation-defined whether any extended alignments are
supported and the contexts in which they are supported
So, the returned pointer by operator new
must have fundamental alignment. Even if zero size specified. And it is implementation defined, whether 8 is fundamental or extended alignment. If it is fundamental, then Foo
is OK. If it is extended, then it is implementation defined that Foo
is supported with operator new
.
Note, that for C++17, the situation is improved:
basic.stc.dynamic.allocation/2 of C++17:
The allocation function attempts to allocate the requested amount of
storage. If it is successful, it shall return the address of the start
of a block of storage whose length in bytes shall be at least as large
as the requested size. There are no constraints on the contents of the
allocated storage on return from the allocation function. The order,
contiguity, and initial value of storage allocated by successive calls
to an allocation function are unspecified. The pointer returned shall
be suitably aligned so that it can be converted to a pointer to any
suitable complete object type ([new.delete.single]) and then used to
access the object or array in the storage allocated (until the storage
is explicitly deallocated by a call to a corresponding deallocation
function). Even if the size of the space requested is zero, the
request can fail. If the request succeeds, the value returned shall be
a non-null pointer value ([conv.ptr]) p0 different from any previously
returned value p1, unless that value p1 was subsequently passed to an
operator delete. Furthermore, for the library allocation functions in
[new.delete.single] and [new.delete.array], p0 shall represent the
address of a block of storage disjoint from the storage for any other
object accessible to the caller. The effect of indirecting through a
pointer returned as a request for zero size is undefined.
I've put emphasis on the relevant part. That sentence means that the returned pointer of void *operator new(...)
should have suitable alignment. It doesn't mention zero size as a special case (but, of course, it is UB to dereference the returned pointer).
So the answer is the usual, there is no special handling of zero:
void *operator new(std::size_t)
must return an aligned pointer of alignof(std::max_align_t)
void *operator new(std::size_t, std::align_val_t align)
must return an aligned pointer of align
)
Note that it is implementation defined, which version will be called for Foo
. It depends on whether 8 is equal or less than alignof(std::max_align_t)
. If it is less, then the 1st version is called (because it doesn't have extended alignment). Otherwise the 2nd is called.
UPDATE: As Massimiliano Janes comments, these paragraphs apply to the result of operator new
, not to the result of new expression. An implementation could add an arbitrary offset to the result of operator new[]
. And the standard is silent about the value of this x
offset:
new T[5] results in one of the following calls:
operator new[](sizeof(T) * 5 + x)
operator new[](sizeof(T) * 5 + x, std::align_val_t(alignof(T)))
Here, each instance of x is a non-negative unspecified value
representing array allocation overhead; the result of the
new-expression will be offset by this amount from the value returned
by operator new[]. This overhead may be applied in all array
new-expressions, including those referencing the library function
operator new[](std::size_t, void*) and other placement allocation
functions. The amount of overhead may vary from one invocation of new
to another.
However, in my opinion, this x
offset cannot be arbitrary. If it is not a multiple of alignment, then the new expression would return a non-aligned pointer (in all cases. Not just the zero, but the non-zero size parameter as well). That's clearly not we want.
So I think this is a hole in the standard. Value of x
should be constrained to be a multiple of alignment (at least in the non-zero allocation case). But because of this omission, it seems that the standard doesn't guarantee that a new[]
expression returns an aligned pointer at all (in the non-zero case as well).