Legality of using operator delete on a pointer obtained from placement new
I'm dang certain that this code ought to be illegal, as it clearly won't work, but it seems to be allowed by the C++0x FCD.
class X { /* ... */};
void* raw = malloc(sizeof (X));
X* p = new (raw) X(); // according to the standard, the RHS is a placement-new expression
::operator delete(p); // definitely wrong, per litb's answer
delete p; // legal? I hope not
Maybe one of you language lawyers can explain how the standard forbids this.
There's also an array form:
class X { /* ... */};
void* raw = malloc(sizeof (X));
X* p = new (raw) X[1]; // according to the standard, the RHS is a placement-new expression
::operator delete[](p); // definitely wrong, per litb's answer
delete [] p; // legal? I hope not
This is the closest question I was able to find.
EDIT: I'm just not buying the argument that the standard's language restricting arguments to function void ::operator delete(void*)
apply in any meaningful way to the operand of delete
in a delete-expression. At best, the connection between the two is extremely tenuous, and a number of expressions are allowed as operands to delete
which are not valid to pass to void ::operator delete(void*)
. For example:
struct A
{
virtual ~A() {}
};
struct B1 : virtual A {};
struct B2 : virtual A {};
struct B3 : virtual A {};
struct D : virtual B1, virtual B2, virtual B3 {};
struct E : virtual B3, virtual D {};
int main( void )
{
B3* p = new E();
void* raw = malloc(sizeof (D));
B3* p2 = new (raw) D();
::operator delete(p); // definitely UB
delete p; // definitely legal
::operator delete(p2); // definitely UB
delete p2; // ???
return 0;
}
I hope this shows that whether a pointer may be passed to void operator delete(void*)
has no bearing on whether that same pointer may be used as the operand of delete
.
Solution 1:
The Standard rules at [basic.stc.dynamic.deallocation]p3
Otherwise, the value supplied to
operator delete(void*)
in the standard library shall be one of the values returned by a previous invocation of eitheroperator new(size_t)
oroperator new(size_t, const std::nothrow_t&)
in the standard library, and the value supplied tooperator delete[](void*)
in the standard library shall be one of the values returned by a previous invocation of eitheroperator new[](size_t)
oroperator new[](size_t, const std::nothrow_t&)
in the standard library.
Your delete
call will call the libraries' operator delete(void*)
, unless you have overwritten it. Since you haven't said anything about that, I will assume you haven't.
The "shall" above really should be something like "behavior is undefined if not" so it's not mistaken as being a diagnosable rule, which it isn't by [lib.res.on.arguments]p1. This was corrected by n3225 so it can't be mistaken anymore.
Solution 2:
The compiler doesn't really care that p
comes from a placement new
call, so it won't prevent you from issuing delete
on the object. In that way, your example can be considered "legal".
That won't work though, since "placement delete
" operators cannot be called explicitly. They're only called implicitly if the constructor throws, so the destructor can run.
Solution 3:
I suppose you might get away with it (on a particular compiler) if
-
new
/delete
are implemented in terms ofmalloc
/free
and - the placement
new
actually uses the same mechanism for keeping track of the destructor associated with allocations as the standardnew
, so that the call todelete
could find the right destructor.
But there is no way it can be portable or safe.