array new-expressions: proposal

Matt Austern austern at isolde.engr.sgi.com
Fri Jan 21 18:16:55 UTC 2000


WHAT THE STANDARD SAYS. (3.7.3.1, 5.3.4, and 18.4.1.3)

Array new has the form "new(ARGS) T[n]".  The "(ARGS)"
part is optional.  If it's present then this is a placement
new-expression, and we use a version of operator new[] with
two or more arguments, otherwise it's an ordinary new-
expression, and we use a version of operator new[] with one
argument.  For the purposes of this proposal the distinction
isn't all that important.

After finding the appropriate operator new, a new-expression
obtains storage with
    void* p = operator new[](n1, ARGS),
where n1 >= n * sizeof(T).  It then constructs n objects of type
T starting at position p1, where p1 = p + delta.  The return
value is p1.

It is required (3.7.3.1/2) that the return value of any operator
new[], whether it's built-in or provided by the user, must be
suitably aligned for objects of any type.

If T is "char" or "unsigned char" the standard requires that
delta is a nonnegative multiple of the most stringent alignment
constraint for objects of size less than or equal to n
(5.3.4/10).  Otherwise the only restriction is that delta is
nonnegative.

Some implementations store the number of elements in the array at
a negative offset from p1.  The standard neither requires nor
forbids it.

There's a predefined placement version of array operator new,
    ::operator new[](size_t n1, void* p),
that does nothing but return p.  p must be a pointer to the
beginning of some array of size at least n1.  The standard
doesn't tell users how large an array they need.  Many users
probably assume that it's sufficient for the array to be of size
n * sizeof(T), but there's no basis in the standard for that
assumption.

IA-64 SPECIFICS

On IA-64 long double is 80 bits.  long double has 128-bit alignment,
as do classes and unions containing long double, so sizeof(long double)
is 16.  All other types have 64-bit alignment.

WHAT THE ABI NEEDS TO SPECIFY

(1) Given n, and T, what are n1 and delta?
    (a) Are T=char and T=unsigned char special cases?  (Or,
        perhaps, is sizeof(T)=1 a special case?)
    (b) Is ::operator new[](size_t, void*) a special case?
    (c) Is ::operator new[](size_t), which is used for
        non-placement new, a special case?
    (d) Is ::operator new[](size_t, const nothrow_t&) a
        special case?  I can't find anything in the standard
        guaranteeing that you can delete an array allocated
        with nothrow array new using an ordinary array delete-
        expression, but users probably expect it, and
        legitimately so.
(2) Do we store n at a negative offset from p1?  (This affects
    the answer to question 1.)  If so, we need to specify
    precisely what that offset is.


PROPOSAL A.

No version of operator new[] is a special case.  For any array
new-expression we store the number of elements in the array,
as a size_t, at an offset of -sizeof(size_t) from the pointer
returned by the new-expression.  For any type T other than char,
unsigned char, long double, or a type containing a long double,
n1 = n * sizeof(T) + sizeof(size_t).   For those three types,
since we need to preserve long double alignment, n1 = n * sizeof(T) +
sizeof(long double).

Pseudocode for new(ARGS) T[n] under this proposal:

    if T = char or unsigned char, or if it has long double alignment,
      padding = sizeof(long double)
    else
      padding = sizeof(size_t)

    p = operator new[](n * sizeof(T) + padding, ARGS)

    p1 = (T*) (p + padding)
    ((unsigned long*) p1 - 1) = n

    for i = [0, n)
      create a T, using the default constructor, at p1[i]

    return p1


PROPOSAL B.

::operator new[](size_t, void*) is a special case.  For that
version of operator new[] only, n1 = n * sizeof(T).  We do not
store the number of elements in such an array anywhere.

Pseudocode for new(ARGS) T[n] under this proposal:

    If the expression is new(p) T[n], and if overload resolution
    determines we're using ::operator new[](size_t, void*), then
      p1 = (T*) p

      for i = [0, n)
        create a T, using the default constructor, at p1[i]

      return p1

    For all other cases, same as proposal A.


Proposal A is simpler, but proposal B probably conforms more
closely to user expectations.




More information about the cxx-abi-dev mailing list