Legality of using delete-expression on a pointer not obtained from usual new
Asked Answered
F

1

6

I wonder if this code is legal:

delete new (operator new(1)) char;

This code does the same thing, but doesn't use delete expression on a pointer obtained from placement new:

void* p = operator new(1);
new (p) char;
delete static_cast<char*>(p);

The standard rules at [expr.delete#2]:

In a single-object delete expression, the value of the operand of delete may be a null pointer value, a pointer value that resulted from a previous non-array new-expression, or a pointer to a base class subobject of an object created by such a new-expression. If not, the behavior is undefined. In an array delete expression, the value of the operand of delete may be a null pointer value or a pointer value that resulted from a previous array new-expression.68 If not, the behavior is undefined.

The standard does not say that new-expression does not include placement new. So I think according to the standard, the first piece of code should be legal, but the second should be illegal. Is my idea correct?

Why the standard says that? Calling operator new and constructing an object on it does almost the same thing as the new expression, but using the delete expression on the obtained pointer is not necessarily legal.

For allocating an array, the standard seems to allow this:

delete[] new (operator new[](2)) char[2];

But it should be illegal due to unspecified overhead.

Why does the standard allow that?


EDIT: According to [intro.object]/13:

Any implicit or explicit invocation of a function named operator new or operator new[] implicitly creates objects in the returned region of storage and returns a pointer to a suitable created object.

So I can even write the following code:

delete static_cast<char*>(operator new(1));

It destroys the implicitly created char object and deallocates the memory, doing the same thing as the examples above. But it's definitely illegal according to the standard because no new-expression is used.

Why doesn't the standard allow that?

Fowling answered 19/8, 2022 at 14:50 Comment(8)
That's actually implementation-defined. If returned value of placement new is same - yes. And type is same here, so ...Shaeffer
delete[] new (operator new(2)) char[2]; isn't legit. Can't delete[] something that was not new[] (as operator new(2) isn't new[]). And the mismatched type for new/delete can't be good either.Baal
@Baal But the standard does not stipulate that delete[] must correspond to new[].Fowling
Carefully re-read your [expr.delete#2] citation.Baal
@Baal Please point it out, I would appreciate it.Fowling
It stipulates that stipulate that delete[] must correspond to new[].Baal
But "array new-expression" != new[]Fowling
While it might works in some cases, you can surely have undefied behavior in more complex cases like when operator new/delete is customized for a class type. You should always destroy objects using matched allocation/desallocation. Manual memory allocation is already tricky and should generally be avoided by using higher level constructs like std::unique_ptr or for arrays std::vector and for strings std::string.Dictum
M
2

delete new (operator new(1)) char; does appear to be legal. Like you said, the standard does not make any exceptions for placement new.

Your second example is also legal:

void* p = operator new(1);
new (p) char;
delete static_cast<char*>(p);

Let's walk through what happens step by step. The call to operator new implicitly creates a char object and returns a pointer to said object1. So p starts out already pointing to a char object. The placement new expression constructs a new char object over the old one, ending the lifetime of the old object; according to the rules of the abstract machine, this magically changes the value of p such that it points to this new object; see [basic.life]/8. Finally, static_cast<char*>(p) returns a pointer pointing to that same char object. That is, this pointer has the same value as the pointer that was returned by the placement new. So if the first example is valid, so is this one.

Note that [expr.delete]/2 does not suggest that in order for an operand to delete to be valid, there must be some sort of valid "chain of custody" from a new expression to the value that is now being deleted. It merely says "... a pointer value that resulted from a previous non-array new-expression..." The value of the pointer is all that matters. In your second example, p has the same value as the pointer returned from the placement new, thanks to [basic.life]/8.

Regarding your third example, delete[] new (operator new(2)) char[2];, I agree that it shouldn't be legal. I suggest filing a defect report.

1 See [intro.object]/13.

Mertiemerton answered 20/8, 2022 at 1:22 Comment(6)
But a delete statement does two things - it calls the object destructor, which should be OK, and returns the memory to the heap, which in the case of a placement new should be a big no-no.Incendiary
@Mark Ransom But the memory itself is from heap because it's allocated by operator new.Fowling
@MarkRansom The arguments go deeper than the two points you mentioned. All have to be considered. Implementation reason: There may be additional metadata stored, that is why new [] and delete[] may be not compatible to new and delete. Logical/optimizer reason: a) A pointer to one type is not in all cases equally valid as pointer to another type, see e.g. aliasing and splicing. b) In C++ there is the notion of lifetime, which officially has to start and end except for implicit-lifetime types. Standard reason: Then there is the specific wording about delete in the standard.Lacreshalacrimal
this pointer has the same value as the pointer that was returned by the placement new Not sure if «a pointer value that resulted from» is equivalent to «a pointer value which is the same as resulted from», i.e. «a pointer value that resulted from» may be read as allowing to track not only the identity of an object pointed to, but also the provenance of such a pointer valueShrieval
@LanguageLawyer The value of a pointer already includes its provenance; a pointer representing the address of an object o but lacking appropriate provenance may fail to have the value "pointer to o". I think it's a stretch to read into the standard an additional layer of provenance. There are no rules in the standard governing such a hypothetical concept.Mertiemerton
@Language Lawyer I don't understand the use of tracking the provenance of such a pointer value, I think it's legal as long as the memory pointed to by the pointer used on the delete-expression was allocated by operator new and the pointer points to a suitable created object.Fowling

© 2022 - 2025 — McMap. All rights reserved.