Current draft standard explicitly states that placement new[]
can have a space overhead:
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.
So presumably they have something in mind, why a compiler need this overhead. What is it? Can a compiler use this overhead for anything useful?
In my understanding, to destruct this array, the only solution is to call destructors in a loop (am I right on this?), as there is no placement delete[]
(btw, shouldn't we have placement delete[]
to properly destruct the array, not just its elements?). So the compiler doesn't have to know the array length.
I thought as this overhead cannot be used for anything useful, compilers don't use it (so this is not an issue in practice). I've checked compilers with this simple code:
#include <stdio.h>
#include <new>
struct Foo {
~Foo() { }
};
int main() {
char buffer1[1024];
char buffer2[1024];
float *fl = new(buffer1) float[3];
Foo *foo = new(buffer2) Foo[3];
printf("overhead for float[]: %d\n", (int)(reinterpret_cast<char*>(fl) - buffer1));
printf("overhead for Foo[] : %d\n", (int)(reinterpret_cast<char*>(foo) - buffer2));
}
GCC and clang doesn't use any overhead at all. But, MSVC uses 8 bytes for the Foo
case. For what purpose could MSVC use this overhead?
Here's some background, why I put this question.
There were previous questions about this subject:
- Array placement-new requires unspecified overhead in the buffer?
- Can placement new for arrays be used in a portable way?
As far as I see, the moral of these questions is to avoid using placement new[]
, and use placement new
in a loop. But this solution doesn't create an array, but elements which are sitting next to each other, which is not an array, using operator[]
is undefined behavior for them. These questions are more about how to avoid placement new[]
, but this question is more about the "why?".
Current draft standard explicitly states ...
To clarify, this rule has (probably) existed since first version of the standard (earliest version I have access to is C++03, which does contain that rule, and I found no defect report about needing to add the rule).
So presumably they have something in mind, why a compiler need this overhead
My suspicion is that the standard committee didn't have any particular use case in mind, but added the rule in order to keep the existing compiler(s?) with this behaviour compliant.
For what purpose could MSVC use this overhead? "why?"
These questions could confidently be answered only by the MS compiler team, but I can propose a few conjectures:
The space could be used by a debugger, which would allow it to show all of the elements of the array. It could be used by an address sanitiser to verify that the array isn't overflowed. That said, I believe both of these tools could store the data in an external structure.
Considering the overhead is only reserved in the case of non-trivial destructor, it might be that it is used to store the number of elements constructed so far, so that compiler can know which elements to destroy in the event of an exception in one of the constructors. Again, as far as I know, this could just as well be stored in a separate temporary object on the stack.
For what it's worth, the Itanium C++ ABI agrees that the overhead isn't needed:
No cookie is required if the new operator
being used is ::operator new[](size_t, void*)
.
Where cookie refers to the array length overhead.
The dynamic array allocation is implementation-specific. But ont of the common practices with implementing dynamic array allocation is storing its size before its beginning (I mean storing size before first element). This perfectly overlaps with:
representing array allocation overhead; the result of the
new-expression will be offset by this amount from the value returned
by operator new[].
"Placement delete" would not make much sense. What delete
does is call destructor and free memory. delete
calls destructor on all of the array elements and frees it. Calling destructor explicitly is in some sense "placement delete".
Current draft standard explicitly states that placement new[] can have a space overhead ...
Yes, beats the hell out of me too. I posted it (rightly or wrongly) as an issue on GitHub, see:
https://github.com/cplusplus/draft/issues/2264
So presumably they have something in mind, why a compiler need this overhead. What is it? Can a compiler use this overhead for anything useful?
Not so far as I can see, no.
In my understanding, to destruct this array, the only solution is to call destructors in a loop (am I right on this?), as there is no placement delete[] (btw, shouldn't we have placement delete[] to properly destruct the array, not just its elements?). So the compiler doesn't have to know the array length.
For the first part of what you say there, absolutely. But we don't need a placement delete []
(we can just call the destructors in a loop, because we know how many elements there are).
I thought as this overhead cannot be used for anything useful, compilers don't use it (so this is not an issue in practice). I've checked compilers with this simple code:
...
GCC and clang doesn't use any overhead at all. But, MSVC uses 8 bytes for the Foo case. For what purpose could MSVC use this overhead?
That's depressing. I really though that all compilers wouldn't do this because there's no point. It's only used by delete []
and you can't use that with placement new
anyway, so...
So, to summarise, the purpose of placement new [ ]
should be to let the compiler know how many elements there are in the array so that it knows how many constructors to call. And that's all it should do. Period.
(Edit: added more detail)
But this solution doesn't create an array, but elements which are sitting next to each other, which is not an array, using operator[] is undefined behavior for them.
As far as I understand, this is not quite true.
[basic.life]
The lifetime of an object of type T begins when:
(1.1) — storage with the proper alignment and size for type T is obtained, and
(1.2) — if the object has non-vacuous initialization, its initialization is complete
Initialisation of an array consists of initialisation of its elements. (Important: this statement may not be directly supported by the standard. If it is indeed not supported, then this is a defect in the standard which makes creation of variable length arrays other than by new[]
undefined. In particular, users cannot write their own replacement for std::vector
. I don't believe this to be the intent of the standard).
So whenever there is a char
array suitably sized and aligned for an array of N
objects of type T
, the first condition is satisfied.
In order to satisfy the second condition, one needs to initialise N
individual objects of type T
. This initialisation may be portably achieved by incrementing the original char
array address by sizeof(T)
at a time, and calling placement new
on the resulting pointers.