Dynamic memory allocation for over-aligned data

Problem statement

To codify widespread existing practice,
C++11 added the ability to specify increased alignment
(a.k.a. over-alignment) for class types.
Unfortunately (but also consistently with existing practice),
C++11 did not specify any mechanism
by which over-aligned data can be dynamically allocated correctly
(i.e. respecting the alignment of the data).
For example:

In this example,
not only is an implementation of C++ not required
to allocate properly-aligned memory for the array,
for practical purposes it is very nearly required
to do the allocation incorrectly.
In any event,
it is certainly required to perform the allocation by a process
that does not take the specified alignment
value into account.

This represents a hole in the support for alignment in the language,
which really needs to be filled.

History

Significant changes in this revision
relative to P0035R1,
including all changes in the proposed wording,
are enclosed in boxes.

Intel has released a compiler
that largely implements the language changes discussed herein,
except that, to guarantee backward compatibility,
the additional overloads are declared in a new header
(<aligned_new>),
instead of being predeclared or
declared in <new>.

To date,
there has not yet been enough experience with the implementation
to prove its viability.
Nevertheless, it seems appropriate
to get this issue back on the committee's radar,
so that a decision can be made about it
for the C++17 time frame.

Acknowledgements

Pablo Halpern has provided me with much valuable feedback
and assistance.

Design considerations

Backward compatibility

One of the first questions that needs to be settled
about the future direction is
the degree to which backward compatibility with C++11/14
needs to be maintained.
On the one hand, in an ideal world, for an example like the one above,
it would be obvious that the specified alignment
should be honored.

On the other hand,
there's no way to achieve that ideal
without at least potentially changing the behavior
of some programs conforming to an earlier standard.
For example, a program might asssume control of dynamic allocation
through the use of class-specific operator new
and operator delete functions,
or by replacing the global functions.
These functions don't take any alignment argument.
If a different function is used instead,
which is somehow passed an alignment value,
some degree of backward compatibility is lost.

When backward compatibility
and the ideal future direction are in conflict,
which should take precedence, and to what degree?

If perfect backward compatibility were required,
one way to ensure that might be to require that a new header
– say <aligned_new> –
be included in order to get new dynamic allocation
for over-aligned types.
But that would sacrifice convenience and/or correctness;
using alignas by itself would presumably never be enough
to get correctly aligned dynamic allocation.

Another obvious position to take
would be that backward compatibility with C++98,
which had no alignment specifier, needs to be complete.
This might suggest that dynamic allocation should differ
between types involving alignment specifiers
and types that don't –
which some might consider to be an unfortunate complication.

In C++11/14, when an over-aligned class type
has its own dynamic memory allocation functions,
it would be reasonable to hope
that those functions already do the right thing
with respect to alignment, and dangerous to make any change.
However, the only way over-alignment could be accommodated
by global allocation and deallocation functions
would be to replace them with functions
that always provide the strictest alignment
used by any type in the program.
It may be reasonable to assume
that very few programs go to that length,
instead of using class-specific allocation/deallocation.

Therefore, it may be acceptable to abandon backward compatibility
with C++14 with respect to calling a global allocation function
for dynamic allocation of an over-aligned type.
But if so, that may well be the only acceptable case.

Passing the alignment value

To minimize the possibility of conflict
with existing placement allocation functions,
it might be advisable to invent a new standard enumeration type
to use for alignment parameters; for example:

It's not clear that this type would need
any named constants of its own;
it just needs to be able to represent alignment values,
which are associated with type size_t.
It should perhaps nevertheless be a scoped enumeration,
to prevent the possibility that a value of that type
would inadvertently be converted to some integer type,
and match an existing placement allocation function.

If an allocation function that takes an alignment value is available,
it should be used, for the sake of generality;
but if no such function is available,
a function that doesn't take one should be used,
for backward compatibility.
This suggests a new rule for new-expressions:
attempting to find an allocation function in two phases,
with two different sets of arguments.

Class-specific allocation and deallocation

It should be kept in mind that,
under the current language rules,
any class-specific allocation functions effectively hide
all global allocation functions,
including the ones in the standard library.
For example, the following is invalid:

It is possible to imagine adjusting the rules
to enable finding an alignment-aware allocation function more often,
but that would also make it more likely
that some programmers would write programs believing
– incorrectly –
that they have taken over complete control
of the way that their class is dynamically allocated.

Unified vs. distinct arenas

What implementation techniques should the standard allow
for allocation and deallocation of aligned memory?

In POSIX, there is a function named posix_memalign
that can allocate over-aligned memory;
free is used to free the blocks it allocates.

On Windows, on the other hand, of course malloc,
realloc and free are supported
for default-aligned memory.
In addition, for over-aligned memory, there are functions named
_aligned_malloc, _aligned_realloc,
and _aligned_free.
Memory that's allocated by _aligned_malloc
must be freed by _aligned_free,
and memory that's allocated by malloc
must be freed by free.
So logically, there are two disjoint, non-interoperable memory arenas;
the program has to know to which arena a block belongs
(i.e. how it was allocated)
in order to be able to free it.

This is almost certain to be true of any implementation
where over-aligned memory allocation is layered
on top of “plain old” default-aligned memory allocation.
There are probably many such implementations,
and they're not likely to go away soon.

In an environment where information about the method used
to allocate a block of memory can be lost,
having distinct arenas (i.e. distinct deallocation functions)
could be inconvenient.
A program whose operation depends on the assumption
that operator new is equivalent to malloc
is effectively an environment where information about the method
used to allocate a block of memory is lost.

But in a well-written, portable C++ program,
at the point where memory is deallocated,
the type of the object being deleted –
and therefore whether it is over-aligned – is known.
This knowledge could, and probably should,
be used to support layered implementations
of over-aligned memory allocation.

This implies that, just as a new-expression for an over-aligned type
should look for an alignment-aware allocation function,
so should a delete-expression for a pointer to an over-aligned type
look for an alignment-aware deallocation function.
Presumably this would be done by selecting a deallocation function
to which the alignment value can be passed,
even though probably very few implementations
will actually have any use for that value.

Nitty-gritty

For exactly what classes should the allocation method change?
Plausible answers include:

Those affected by an alignas
that actually specifies over-alignment.

Those affected by an explicit alignas,
even if the alignment value is basic (i.e. small).

The first answer seems to be right from a pragmatic perspective,
but one consequence is that the behavior of a program might depend
(in a new way)
on an implementation-defined parameter.
But if the only difference between alignment-aware
and alignment-unaware
allocation/deallocation functions
is the actual allocation mechanism
(i.e., in a well-designed program),
this should not be a problem.
It's rather like the implementation's license
to elide certain copies,
which implies that a copy constructor
had really better just make a copy.

The below WD changes use the first answer,
through use of “over-aligned”.
The Intel implementation uses the first answer by default,
but has a command-line option to select the second answer,
for the sake of experimentation.

Also, it should be noted that the over-alignment threshold
used by the Intel implementation doesn't exactly match
the standard's definition of basic alignment.
The threshold used is actually the alignment
observed to be guaranteed
by the implementation of malloc
for the target environment.
(In all of the environments tested,
this turned out to be twice the size of a pointer.)

Assuming the existence of a variety of allocation functions,
which one should be used for an over-aligned allocation?
I believe the answer should be
the first one from the following list that is known to exist:

class-specific and alignment-aware

class-specific and alignment-unaware

global and alignment-aware

[global and alignment-unaware]

It makes sense for a class-specific,
alignment-unaware allocation function to be preferred
over one that is global and alignment-aware,
because there are many cases
where a class-specific allocation function has enough information,
even without an explicit parameter,
to do the allocation with sufficient alignment.
(Likely exceptions include
a template class with a base or member
of a type that is a template parameter,
and a derived class that inherits its allocation function
from a base class,
and also adds a member or base of over-aligned type.)

If a global, alignment-aware allocation function is predeclared,
then it will never be necessary
to use a global, alignment-unaware allocation function
for an over-aligned type;
hence the brackets around item 4.

It should be noted that an alignment-aware allocation function
would be perfectly capable of performing an alignment
that would suffice for an alignment-unaware function.
In other words, from some perspective,
it would make sense to let operator new(size_t)
call operator new(size_t, align_val_t),
filling in the alignment value that it feels it needs to satisfy,
and move the allocation loop to the alignment-aware function.
But that would be pretty novel,
so I have chosen not to propose it.

The allocation alignment threshold

At a joint EWG/CWG session in Jacksonville,
it was agreed that the threshold above which to use
the new allocation routines should be
the maximum alignment provided
by the existing allocation routines,
and that this value should be made available
to the programmer.

In this revision, I have chosen to use a predefined macro
for this purpose.

Unquestionably, the compiler needs to know this value.
It's possible to imagine some magical way it could be learned
from some library header,
but it needs to be known
even if no header has been included.
And given the existence of independent implementations
of the standard C++ library,
pragmatically sometimes it will be the programmer who needs to
inform the compiler of this value at build time.
To me, this set of requirements really suggests something
an awful lot like a predefined macro.

There is no reason why this value couldn't also be exposed
(with a “nicer” name)
by something in a library header.
But I'm afraid that in real-world implementations,
the actual value from the library would be provided
by some macro anyway.

So really, the only question is whether
the predefined macro name appears in the standard,
or is left for implementations to pick.
And I think library implementers would be grateful
if they didn't have to deal with
different predefined macro names for different compilers.

LEWG will eventually consider whether
an additional non-macro name for this is justified.
The absence of any such proposal in this revision
should be taken as a sign of caution (or laziness)
on my part, and not of prejudice or opposition.

Proposed working draft changes

The following changes are relative to N4582 (post-Jacksonville).

There is one change of terminology worth noting.
Today, the phrase “placement new” is ambiguous.
In some contexts it means adding arguments
to a call to an allocation function,
with any types and unspecified purpose.
In other contexts,
it is used to refer specifically to cases
where there is a single additional argument
of type void *,
in which case the allocation function
doesn't actually allocate anything.
I refer to the latter cases as “non-allocating”,
and refer to “allocating” cases
to distinguish them when necessary.

Change 3.7.4p1:

Objects can be created dynamically
during program execution (1.9),
using new-expressions (5.3.4), and
destroyed using delete-expressions (5.3.5).
A C++ implementation provides access to,
and management of, dynamic storage
via the global allocation functionsoperator new and operator new[]
and the global deallocation functionsoperator delete
and operator delete[].
[ Note: The non-allocating forms
described in [new.delete.placement]
do not perform allocation or deallocation.
—end note]

This is intended as a clarification
of what seems already to be implied by [new.delete.placement]:

The provisions of (3.7.4) do not apply
to these reserved placement forms of
operator new and operator delete.

Change 3.7.4p2:

The library provides default definitions
for the global allocation and deallocation functions.
Some global allocation and deallocation functions
are replaceable (18.6.1).
A C++ program shall provide at most one definition
of a replaceable allocation or deallocation function.
Any such function definition replaces the default version
provided in the library (17.6.4.6).
The following allocation and deallocation functions (18.6)
are implicitly declared in global scope
in each translation unit of a program.

These implicit declarations introduce only the function names
operator new, operator new[],
operator delete, and operator delete[].
[ Note: The implicit declarations do not introduce
the names std,
std::size_t,
std::align_val_t,
or any other names that the library uses to declare these names.
Thus, a new-expression, delete-expression
or function call that refers to one of these functions
without including the header <new>
is well-formed.
However, referring to std or
std::size_tor std::align_val_t
is ill-formed unless the name has been declared
by including the appropriate header.
—end note]
Allocation and/or deallocation functions canmay also be declared and defined
for any class (12.5).

The set of added functions is unchanged,
but it was previously presented as a single cluster;
the difference is editorial,
but this ordering probably makes more sense.
OTOH, a different order may make even more sense;
see 18.6.1.

For deallocation, I originally chose
to put the alignment before the size
because the size argument is necessarily just a hint,
whereas if separate arenas are used,
at least the presence or absence of an alignment argument
is significant for correctness.
In a CWG teleconference,
a mild consensus preference was expressed that
when a deallocation function takes both an alignment and a size,
the size should precede the alignment,
mainly because that's the order
when an allocation function takes both.
LEWG needs to decide which order it prefers.

Change 3.7.4.1p2:

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 ofto any suitable complete object type
([new.delete.single])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.
Furthermore, for the library allocation functions
in 18.6.2.1 and 18.6.2.2,
p0 shall point to 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.36

The text here is intentionally made less specific,
with the details to be found elsewhere.

Change 3.7.4.2p2:

Each deallocation function shall return void
and its first parameter shall be void*.
A deallocation function canmay have
more than one parameter.
The global operator delete
with exactly one parameter
is a usual (nonplacement) deallocation function.
The global operator delete
with exactly two parameters,
the second of which has type std::size_t,
is a usual deallocation function.
Similarly, the global operator delete[]
with exactly one parameter
is a usual deallocation function.
The global operator delete[]
with exactly two parameters,
the second of which has type std::size_t,
is a usual deallocation function.37
If a class T
has a member deallocation function
named operator delete
with exactly one parameter,
then that function is a usual deallocation function.
If class T does not declare
such an operator delete but does declare
a member deallocation function
named operator delete
with exactly two parameters,
the second of which has type std::size_t,
then this function is a usual deallocation function.
Similarly, if a class T
has a member deallocation function
named operator delete[]
with exactly one parameter,
then that function is
a usual (non-placement) deallocation function.
If class T does not declare
such an operator delete[] but does declare
a member deallocation function
named operator delete[]
with exactly two parameters,
the second of which has type std::size_t,
then this function is a usual deallocation function.A usual deallocation function
is a deallocation function that has:

exactly one parameter; or

exactly two parameters,
the type of the second being
either std::align_val_t
or std::size_t37); or

exactly three parameters,
the type of the second being std::align_val_t
and the type of the third
being std::size_t.

A deallocation function canmay
be an instance of a function template.
Neither the first parameter nor the return type
shall depend on a template parameter.
[ Note: That is, a deallocation function template
shall have a first parameter of type void*
and a return type of void
(as specified above). —end note ]
A deallocation function template
shall have two or more function parameters.
A template instance is never a usual deallocation function,
regardless of its signature.

Adding the new overloads
to the set of "usual" deallocation functions
in the same style as the previous formulation
would have required outrageous verbosity.
My new formulation is much more concise
and (to my eyes) comprehensible,
but there is a technical difference worth noting.

Previously, in class scope, if deallocation functions
with and without a size parameter are both declared,
the one with the size parameter was not "usual".
My simpler formulation includes both overloads.
But it is not difficult to tweak the selection algorithm
(5.3.5p10)
to produce the same result as previously.

Change 3.7.4.2p3:

If a deallocation function terminates by throwing an exception,
the behavior is undefined.
The value of the first argument
supplied to a deallocation function
may be a null pointer value;
if so, and if the deallocation function
is one supplied in the standard library,
the call has no effect.
Otherwise, the behavior is undefined
if the value supplied to operator delete(void*)
in the standard library is not one of the values
returned by a previous invocation
of either operator new(std::size_t)
or operator new(std::size_t,
const std::nothrow_t&)
in the standard library, and the behavior is undefined
if the value supplied to operator delete[](void*)
in the standard library is not one of the values returned
by a previous invocation
of either operator new[](std::size_t)
or operator new[](std::size_t,
const std::nothrow_t&)
in the standard library.

These requirements apply only to the library implementations,
and are already stated in 18.6.

Change 3.7.4.3p2:

A pointer value is a safely-derived pointer
to a dynamic object only if
it has an object pointer type and it is one of the following:

the value returned by a call
to the C++ standard library implementation of
::operator new(std::size_t)or ::operator new(std::size_t,
std::align_val_t);39

...

Change 3.9.2p3:

... [ Note:
Pointers to over-aligned types (3.11)
have no special representation,
but their range of valid values is restricted
by the extended alignment requirement.
This International Standard specifies only two ways
of obtaining such a pointer:
taking the address of a valid object
with an over-aligned type,
and using one of the runtime pointer alignment functions.
An implementation may provide other means
of obtaining a valid pointer value
for an over-aligned type.
—end note ]

Change 3.11p3:

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 (7.6.2).
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__.
[ Note:
every over-aligned type is or contains a class type to which extended alignment applies (possibly through a
non-static data member).
—end note ]

Change 5.3.4p1:

The new-expression attempts to create
an object of the type-id (8.1)
or new-type-id
to which it is applied.
The type of that object is the allocated type.
This type shall be a complete object type,
but not an abstract class type
or array thereof (1.8, 3.9, 10.4).
It is implementation-defined whether over-aligned types
are supported (3.11). [ Note: ...

Change 5.3.4p8:

A new-expression may obtain storage
for the object by calling an allocation function (3.7.4.1).
If the new-expression terminates
by throwing an exception,
it may release storage
by calling a deallocation function (3.7.4.2).
If the allocated type is a non-array type,
the allocation function's name
is operator new
and the deallocation function's name
is operator delete.
If the allocated type is an array type,
the allocation function's name
is operator new[]
and the deallocation function's name
is operator delete[].
[ Note:
an implementation shall provide default definitions
for the global allocation functions (3.7.4, 18.6.2.1, 18.6.2.2).
A C++ program can provide alternative definitions
of these functions (17.6.4.6)
and/or class-specific versions (12.5).
The set of allocation and deallocation functions
that may be called by a new-expression may
include functions that do not perform allocation or deallocation;
for example, see [new.delete.placement].
—end note ]

As an editorial matter,
note that the current WD inappropriately uses italics
for the first occurrence of “allocation function”
in this paragraph;
it should be removed.

Change 5.3.4p13:

The new-placement syntax is used
to supply additional arguments
to an allocation function;
such an expression is called
a placement new-expression.
If used, overloadOverload resolution
is performed on a function call
created by assembling an argument list.consisting ofThe first argument is the amount of space requested
(the first argument), and has type std::size_t.
If the type of the allocated object
has new-extended alignment,
the next argument is the type's alignment,
and has type std::align_val_t.and theIf the new-placement syntax is used, its
expressions
in the new-placement part
of the new-expression (are thesecond and succeeding arguments).
The first of these arguments has type std::size_t
and the remaining arguments have the corresponding types
of the expressions in the new-placement;
such an expression is called
a placement new-expression.If no matching function is found
and the allocated object type
has new-extended alignment,
the alignment argument is removed from the argument list,
and overload resolution is performed again.

Change 5.3.4p14:

[ Example:

new T results in a call of eitheroperator new(sizeof(T))or operator new(sizeof(T),
std::align_val_t(alignof(T))),

[ Note:
unless an allocation function
has a non-throwing exception specification (15.4),
it indicates failure to allocate storage
by throwing a std::bad_alloc exception
(3.7.4.1, Clause 15, 18.6.3.1);
it returns a non-null pointer otherwise.
If the allocation function
has a non-throwing exception specification,
it returns null to indicate failure to allocate storage
and a non-null pointer otherwise.
—end note ]
If the allocation function is a
reserved placement allocation functionnon-allocating form (18.6.2.3)
that returns null, the behavior is undefined.
Otherwise, if the allocation function returns null,
initialization shall not be done,
the deallocation function shall not be called,
and the value of the new-expression
shall be null.

Change 5.3.4p22:

A declaration of a placement deallocation function
matches the declaration of a placement allocation function
if it has the same number of parameters and,
after parameter transformations (8.3.5),
all parameter types except the first are identical.
If the lookup finds a single matching deallocation function,
that function will be called;
otherwise, no deallocation function will be called.
If the lookup finds the two-parameter form of
a usual deallocation function
with a size parameter (3.7.4.2)
and that function,
considered as a placement deallocation function,
would have been selected as a match
for the allocation function,
the program is ill-formed.
For a non-placement allocation function,
the normal deallocation function lookup is used
to find the matching deallocation function (5.3.5)
[ Example:

Change 5.3.5p5:

If the object being deleted
has incomplete class type at the point of deletion,
and the complete class
has new-extended alignment,
has a non-trivial destructor,
or has a deallocation function,
the behavior is undefined.

Change 5.3.5p10:

If deallocation function lookup finds both
amore than one usual deallocation function
with only a pointer parameter and
a usual deallocation function with both a pointer parameter
and a size parameter,
the function to be called is selected as follows:

If the type has new-extended alignment,
a function with an alignment parameter is preferred;
otherwise a function with no alignment parameter
is preferred.
If exactly one preferred function is found,
that function is selected
and the selection process terminates.
If more than one preferred function is found,
all non-preferred functions
are eliminated from further consideration.

If the deallocation functions have class scope,
the one without a size parameter is selected.

If the type is complete and if,
for the second alternative (delete array) only,
the operand is a pointer to a class type
with a non-trivial destructor
or a (possibly multi-dimensional) array thereof,
the function with two parametersa size parameter is selected.

Otherwise, it is unspecified
which of the two deallocation functionswhether a deallocation function
with a size parameter is selected.

Change 5.3.5p11:

When a delete-expression is executed,
the selected deallocation function shall be called
with the address of the block of storage to be reclaimed
as its first argument.
If a deallocation function with an alignment parameter
is used,
the alignment of the type
of the object to be deleted
is passed
as the corresponding argument.and (if the two-parameterIf a
deallocation function
with a size parameter
is used),
the size of the block is passed
as its secondthe corresponding argument.82

Add a new predefined macro to the list in 16.8p1:

__STDCPP_DEFAULT_NEW_ALIGNMENT__

An integer literal whose value is the threshold
for an alignment value above which
operator new(std::size_t)is not guaranteed
to allocate appropriately-aligned memory.

Change 17.6.4.6p2:

A C++ program may provide the definition
for any of the following
dynamic memory allocation function signatures
declared in header <new> (3.7.4, 18.6):

I have carefully added the new overloads
in the synopsis to appear in the same order
that they are described below.
Unfortunately, that required moving the declarations
of nothrow deallocation functions;
that's an editorial change that I didn't explicitly mark above.
I also noticed that the lists in 3.7.4 and 17.6.4.6
are ordered differently than this one,
and for that matter that the list in 17.6.4.6 has bullets;
editorially, it may be desirable for these lists
to be more consistent with one another.

Change 18.6.2p1:

Except where otherwise specified,
the provisions of (3.7.4) apply
to the library versions of operator new
and operator delete.
If the value of an alignment argument
passed to any of these functions
is not a valid alignment value,
or for an allocation function if the size argument
is not an integral multiple of the alignment argument,
the behavior is undefined.

The provision about the size
being a multiple of the alignment
was added in response to a comment
made at the Jacksonville meeting.
However, Martin Sebor, who has also been working
on aligned allocation for C,
points out that it unnecessarily complicates
the following technique:

It basically assumes that
the only way a block of allocated memory can be used
is by filling it with an array
(of elements all having the same type).
But raw memory is (or at least ought to be)
more flexible than that.
So I think it would make sense to remove that provision,
or change it such that the size must be
at least as great as the alignment.

Change section 18.6.2.1 as follows.
(Some technically irrelevant editorial improvements are included;
in CWG, these would be called “en passant”
or “drive-by” changes.
Also, there are some inappropriate uses
of italics in these two sections which should be fixed,
for phrases including
“allocation function”,
“deallocation function”,
and “ptr”.)

18.6.2.1 Single-object forms

Effects:
The allocation functionfunctions
([basic.stc.dynamic.allocation])
called by a new-expression ([expr.new])
to allocate size bytes of storage.
The second form is called for a type with new-extended alignment,
and allocates storage
with the specified alignment.
The first form is called otherwise,
and allocates storage
suitably aligned to represent any object of that size
or smaller,
provided the object's type does not have
new-extended alignment.

Replaceable:
a C++ program may define
a function with this function signature
that displacesfunctions with either of these function signatures,
and thereby displace
the default versionversions
defined by the C++ standard library.

Required behavior:
Return a non-null pointer
to suitably aligned storage ([basic.stc.dynamic]),
or else throw a bad_alloc exception.
This requirement is binding on aany
replacement versionversions
of this functionthese functions.

Default behavior:

Executes a loop:
Within the loop,
the function first attempts to allocate
the requested storage.
Whether the attempt involves a call
to the Standard C library functionfunctionsmallocor aligned_alloc
is unspecified.

Returns a pointer to the allocated storage
if the attempt is successful.
Otherwise, if the
current new_handler ([get.new.handler]) is
a null pointer value, throws
bad_alloc.

Otherwise, the function calls the current
new_handler function ([new.handler]).
If the called function returns, the loop repeats.

The loop terminates when an attempt to allocate
the requested storage is
successful or when a called
new_handler
function does not return.

Effects:
Same as above, except that it isthese are
called by a placement version of a new-expression
when a C++ program prefers a null pointer result
as an error indication,
instead of a bad_alloc exception.

Replaceable:
a C++ program may define a function
with this function signature
that displaces the default version defined by the
C++ standard library.
This should be changed similarly
to paragraph 2.

Required behavior:
Return a non-null pointer
to suitably aligned storage ([basic.stc.dynamic]),
or else return a null pointer.
ThisEach of these nothrow
versionversions of
operator new
returns a pointer
obtained as if acquired
from the (possibly replaced) ordinary versioncorresponding non-placement function.
This requirement is binding on a
replacement version
of this function.
The last sentence should be changed
similarly to paragraph 3.

Default behavior:
Calls operator new(size),
or operator new(size, alignment), respectively.
If the call returns normally,
returns the result of that call.
Otherwise, returns a null pointer.

Effects:
The deallocation functionfunctions ([basic.stc.dynamic.deallocation])
called by a delete-expression
to render the value of ptr invalid.

Replaceable:
a C++ program may define
a function with signature
void operator delete(void* ptr) noexcept
that displacesfunctions with any of these signatures,
and thereby displace
the default version(s) defined by the
C++ standard library.
If thisa function
(without asize parameter)
is defined,
the program should also define
void operator delete(void* ptr,
td::size_t size) noexceptthe corresponding function
with a size parameter.
If thisa function
with asize parameter is defined,
the program shall also define
the corresponding version
without the size parameter.
[ Note: The default behavior below
may change in the future,
which will require replacing both deallocation functions
when replacing the allocation function.
—end note ]

Requires:ptr shall be a null pointer or
its value shall be a value returnedpoint to a block of memory allocated by an
earlier call to thea (possibly replaced)
operator new(std::size_t)or
operator new(std::size_t,
const std::nothrow_t&std::align_val_t)
which has not been invalidated by an intervening call to
operator delete(void*)or
operator delete(void*, std::size_t).

Requires:
If an implementation
has strict pointer safety ([basic.stc.dynamic.safety])
then ptr shall be a safely-derived pointer.

Requires:If the alignment parameter is not present,
ptr shall have been returned
by an allocation function
not taking an alignment argument.
If present, the alignment argument shall equal
the alignment argument passed to the allocation function
that returned ptr.
If present,
the std::size_t size argument
shall equal the size argument passed to the allocation function
that returned ptr.

Default behavior:
If ptr is null, does nothing.
Otherwise, reclaims the storage allocated
by the earlier call to operator new.

The previous revision suggested changing
“Default behavior” to “Required behavior”.
I am walking back from that suggestion,
not because I think it was wrong,
but simply because it has nothing to do with alignment.
If that should be done, let it be done as a separate effort.

Remarks:
It is unspecified under what conditions part or all of such
reclaimed storage will be allocated by subsequent calls to
operator new
or any of
aligned_alloc,calloc,
malloc,
or
realloc,
declared in
<cstdlib>.

Effects:
The deallocation functionfunctions ([basic.stc.dynamic.deallocation])
called by the implementation
to render the value of ptr invalid
when the constructor invoked
from a nothrow placement version
of the new-expression throws an exception.

Replaceable:
a C++ program may define a functionfunctions with
signature
void operator delete(void* ptr,
const std::nothrow_t&) noexcept
that displacesany of these signatures, and thereby displace
the default version(s)
defined by the C++ standard library.

Requires:
If an implementation
has strict pointer safety ([basic.stc.dynamic.safety])
then ptr shall be a safely-derived pointer.

18.6.2.2 Array forms

Effects:
The allocation functionfunctions ([basic.stc.dynamic.allocation])
called by the array form of a
new-expression ([expr.new])
to allocate size bytes of storage.
The second form is called for a type with new-extended alignment,
and allocates storage
with the specifed alignment.
The first form is called otherwise,
and allocates storage
suitably aligned to represent any array
object of that size or smaller,
provided the array's element type does not have
new-extended alignment.224

Footnote 224: It is not the direct responsibility of
operator new[](std::size_t)
or
operator delete[](void*)
to note the repetition count or element size of the array.
Those operations are performed elsewhere in the array
new
and
delete
expressions.
The array
new
expression, may, however,
increase the size argument to
operator new[](std::size_t)
to obtain space to store supplemental information.

Replaceable:
a C++ program canmay define a
function with this function signature
that displaces the default version
defined by the C++ standard library.
This should be changed
similarly to paragraph 2 above.

Required behavior:
Same as for
operator new(std::size_t)the corresponding single-object forms.
This requirement is binding on a
replacement version
of this function.
The last sentence should be changed
similarly to paragraph 3 above.

Effects:
Same as above, except that it isthese are called by a placement version of a
new-expression
when a C++ program prefers a null pointer result
as an error indication,
instead of a
bad_alloc
exception.

Replaceable:
a C++ program canmay define a
function with this function signature
that displaces the default version
defined by the C++ standard library.
This should be changed
similarly to paragraph 2.

Required behavior:
Return a non-null pointer
to suitably aligned storage ([basic.stc.dynamic]),
or else return a null pointer.
ThisEach of these
nothrow versionversions
of operator new[] returns
a pointer obtained as if
acquired from the (possibly replaced)
operator new[](std::size_t) functioncorresponding non-placement function.
This requirement is binding
on a replacement version of this function.
The last sentence should be changed
similarly to paragraph 3.

Default behavior:
Calls operator new[](size),
or operator new[](size, alignment),
respectively.
If the call returns normally,
returns the result of that call.
Otherwise, returns a null pointer.

Effects:
The deallocation functionfunctions ([basic.stc.dynamic.deallocation])
called by the array form of a
delete-expression
to render the value of ptr invalid.

Replaceable:
a C++ program canmay define
a function with signature
void operator delete[](void* ptr) noexcept
that displacesfunctions with any of these signatures,
and thereby displace
the default version(s)
defined by the C++ standard library.
If thisa function
(without asize
parameter) is defined,
the program should also define
void operator delete[](void* ptr,
std::size_t size) noexceptthe corresponding function
with a size parameter.
If thisa function
with asize parameter is defined,
the program shall also define
the corresponding version
without the size parameter.
[ Note: The default behavior below
may change in the future,
which will require replacing both deallocation functions
when replacing the allocation function.
—end note ]

Requires:ptr shall be a null pointer or its value shall
be the value returnedpoint to a block of memory allocated
by an earlier call to
a (possibly replaced)operator new[](std::size_t)or
operator new[](std::size_t,
const std::nothrow_t&std::align_val_t)
which has not been invalidated by an intervening call to
operator delete[](void*)or
operator delete[](void*,
std::size_t).

Requires:If the alignment parameter is not present,
ptr shall have been returned
by an allocation function
not taking an alignment argument.
If present, the alignment argument shall equal
the alignment argument passed to the allocation function
that returned ptr.
If present,
the std::size_t size argument
mustshall equal the size
argument passed to the allocation function
that returned ptr.

Requires:
If an implementation
has strict pointer safety ([basic.stc.dynamic.safety])
then ptr shall be a safely-derived pointer.

Default behavior:operator delete[](void* ptr,
std::size_t size) calls
operator delete[](ptr), and
operator delete[](void* ptr) calls
operator delete(ptr).The functions that have a size parameter
forward their other parameters
to the corresponding function
without a size parameter.
The functions that do not have
a size parameter
forward their parameters
to the corresponding operator delete
(single-object) function.

This is the one place in the standard
where the default behavior needs to be specified
for four replaceable functions.
My senses of esthetics and propriety rebelled
at the prospect of enumerating them all,
especially in a single flowing paragraph.

Effects:
The deallocation functionfunctions ([basic.stc.dynamic.deallocation])
called by the implementation
to render the value of ptr invalid
when the constructor invoked from a nothrow
placement version of the array new-expression
throws an exception.

Replaceable:
a C++ program may define a functionfunctions with signature
void operator delete[](void* ptr,
const std::nothrow_t&) noexcept
that displacesany of these signatures, and thereby displace
the default version(s) defined by the
C++ standard library.

Requires:
If an implementation
has strict pointer safety ([basic.stc.dynamic.safety])
then ptr shall be a safely-derived pointer.

18.6.2.3 PlacementNon-allocating forms
[new.delete.placement]

Change 20.9.9.1p5:

Returns:
A pointer to the initial element of an array of storage
of size n * sizeof(T),
aligned appropriately for objects of type T.
It is implementation-defined
whether over-aligned types are supported (3.11).

Change 20.9.9.1p6:

Remark: the storage is obtained
by calling ::operator new(std::size_t)
(18.6.2),
but it is unspecified when or how often
this function is called.
The use of hint is unspecified,
but intended as an aid to locality
if an implementation so desires.

Change 20.9.9.1p10:

Remarks:
Uses ::operator delete(void*, std::size_t)
(18.6.2), but it is unspecified when this function is called.

Change 20.9.11p1:

Effects:
Obtains a pointer to uninitialized,
contiguous storage for N adjacent objects
of type T,
for some non-negative number N.
It is implementation-defined
whether over-aligned types are supported (3.11).