Short answer:
There is no direct support for this usage. If you overload new with a different signature, the compiler considers it an overload of new (not placement new) and adds its own book-keeping code. There is no way (I can find) to say to the compiler "unwind your book-keeping, and call my delete overload matching this signature" - it will only insert code to unwind the book-keeping when calling void operator delete(void* p)
or void operator delete[](void* p)
.
If you do override new with a new signature, the compiler likes you to define a delete with matching signature in case of exceptions during new - this is the only time it gets used.
There is no placement delete in the sense that it is not callable, but it is defined in case of exceptions (to do nothing).
Long answer:
This topic raises some interesting points:
- What, exactly, does
void* operator new[](size_t sz, Allocator* a)
overload?
- Is there, or is there not, a "placement delete".
- How does one invoke
void operator delete[](void* p, Allocator* a)
, in such a way as the compiler inserts its book-keeping finalization?
Point 1: Much talk about overloading placement new. Given that the compiler is inserting book keeping code, it must be of the opinion that that void* operator new[](size_t sz, Allocator* a)
declares an overload of (non-placement) new. It will never insert book-keeping code for placement new, because the point of placement new is you are handling it youself.
Point 2: R.E. "no such thing as placement delete", you will find something that looks awfully like it (and commented as such) in e.g. the VS2k8 new header. It is simply a stub used in cases where an exception occurs during placement new. It does however appear to be true that you cannot invoke placement delete in a meaningful way.
Point 3: If there is a way, I can't find it. This is the heart of the problem.
In terms of a practical solution to the problem, it appears to be a bust.
for example:
//intention: user provides memory pool, compiler works out how many bytes required
//and does its own book-keeping, as it would for a void* operator new[](size_t sz) overload
//calling syntax: CObj* pMyArr = new(pMyMemPool) CObj[20];
void* operator new[](size_t sz, IAlloc* pMemPool)
{ return pMemPool->alloc(sz); }
//problem: don't know the syntax to call this!
//e.g. delete[](pMyMemPool) pMyArr is syntax error
void* operator delete[](void* p, IAlloc* pMemPool)
{ return pMemPool->free(p); }
//nb: can be called as operator delete(pMyArr, pMyMemPool);
//but compiler does not finish its book-keeping or call dtors for you in that case.
Note that this asymmetry exists for non-array new & delete too. However, because (empirically) the compiler in question does no extra book-keeping it can all be made to work. Again, if this is enshrined in standard I don't know.
void* operator new(size_t sz, IAlloc* pMemPool)
{ return pMemPool->alloc(sz); }
//don't know syntax to get this called by compiler!
void operator delete(void* p, IAlloc* pMemPool)
{ pMemPool->free(p); }
//is ok though, can work around
template<class T> void tdelete(void* p, IAlloc* pMemPool)
{
//no problems, p points straight at object
p->~T();
operator delete(p, pMemPool);
//OR just
pMemPool->free(p);
}
void* operator new[](size_t sz, IAlloc* pMemPool)
{ return pMemPool->alloc(sz); }
//again, don't know syntax to end up here.
void operator delete[](void* p, IAlloc* pMemPool)
{ pMemPool->free(p); }
//can't work around this time!
template<class T> void tarrdelete(void* p, IAlloc* pMemPool)
{
//problem 1: how many to dtor?
for(int i=0; i<???; ++i)
{ reinterpret_cast<T*>(p+i)->~T(); }
//problem 2: p points at first element in array. this is not always the address
//that was allocated originally.
pMemPool->free(?);
//as already explained by OP, no way to tell if p is address allocated or
//address allocated+4 bytes, or something else altogether. this means no way to know what address to un-alloc or how many dtors to call.
}
Finally, I'll state the obvs. - overloads without the extended parameter list do work:
//sz may include extra for book-keeping
void* operator new[](size_t sz)
{ return GAlloc->alloc(sz); }
//works fine, compiler handled book-keeping and p is the pointer you allocated
void operator delete[](void* p)
{ return GAlloc->free(p); }
Summary: Is there syntax that will allow calls to an overload of delete with an extended parameter list, with compiler "magic" enabled. Or, is there a way to add parameters to placement new by override?
Suspected answer: No.
Corollary: You cannot stray from the 6 built-in new signatures with complete freedom. Doing so results in an overload of new, with compiler generated book-keeping, but no access to the corresponding delete to unwind to book-keeping.
Caveat: You can stray from the built-in signatures, but only to inject code you do not need to handle again at delete (e.g. instrumentation). If you fall through to the void* operator new(size_t s)
version for the allocation, then delete will still work as normal.
(Some statements of fact are drawn from experiments in the debugger and may only apply to MSVC8 (cl9). OP sits on next desk to me.)