Blackteahamburger
Blackteahamburger

Reputation: 215

Why default operator delete[] can't deallocate the memory allocated by default operator new?

Due to unspecified overhead, it is illegal to deallocate with delete-expression that does not match the form of the new-expression.

However, default operator new and operator delete isn't the same.

operator new[]:

[new.delete#array-4]

Default behavior: Returns operator new(size), or operator new(size, alignment), respectively.

operator delete[]:

[new.delete#array-14]:

Default behavior: The functions that have a size parameter forward their other parameters to the corresponding function without a size parameter. The functions that do not have a size parameter forward their parameters to the corresponding operator delete (single-object) function.

It can be seen that default operator new[] and operator delete[] simply call operator new and operator delete.

However, operator delete[] can only deallocate the memory allocated by operator new[], according to [new.delete#array-9]:

Preconditions: ptr is a null pointer or its value represents the address of a block of memory allocated by an earlier call to a (possibly replaced) operator new or operator new[](std​::​size_­t, std​::​align_­val_­t) which has not been invalidated by an intervening call to operator delete[].

Therefore, the following code is illegal:

operator delete[](operator new(1));

Because void* operator new(std::size_t size) does not return void* operator new[](std::size_t size).

However, the following code is legal:

operator delete(operator new[](1));

According to [new.delete#single-10]:

Preconditions: ptr is a null pointer or its value represents the address of a block of memory allocated by an earlier call to a (possibly replaced) operator new(std​::​size_­t) or operator new(std​::​size_­t, std​::​align_­val_­t) which has not been invalidated by an intervening call to operator delete.

void* operator new[](std::size_t size) does return operator new(size), so no problem at all.

Is this asymmetric behavior an issue?

Upvotes: 1

Views: 391

Answers (2)

Blackteahamburger
Blackteahamburger

Reputation: 215

See LWG3789, the status of the issue has been set to NAD:

"No reason to carve out an exception covering a case on something which can’t be observed by the program (whether the allocation operators are replaced). This just makes things more complicated for no good reason." "This would require changes to sanitizers and other dynamic analyzers, for zero practical benefit (except allowing bad code to go un-diagnosed)."

Upvotes: 0

Chris Uzdavinis
Chris Uzdavinis

Reputation: 6131

No it's not a problem.

The default behavior of the compiler implementation does not remove the requirements on calls you make. The requirements as specified in the standard are pretty clear: you cannot mix and match. Even if you think it might work in some case, if you're violating the wording, it is not correct code.

You said this:

However, the following code is legal:

operator delete(operator new[](1));

But that is not correct. You are mismatching calls to operator new[] and operator delete.

Look again at the preconditions for (scalar) operator delete:

Preconditions: ptr is a null pointer or its value represents the address of a block of memory allocated by an earlier call to a (possibly replaced) operator new(std​::​size_­t) or operator new(std​::​size_­t, std​::​align_­val_­t) which has not been invalidated by an intervening call to operator delete.

But you are not calling operator new your example uses operator new[]. They are different operators.

The standard (https://eel.is/c++draft/new.delete#array-9) for preconditions for (array) operator delete[] says this:

Preconditions: ptr is a null pointer or its value represents the address of a block of memory allocated by an earlier call to a (possibly replaced) operator new[](std​::​size_­t) or operator new[](std​::​size_­t, std​::​align_­val_­t) which has not been invalidated by an intervening call to operator delete[].

(emphasis added again)

It requires and only is valid with pointers obtained from calls to operator new[]


Also

void* operator new[](std::size_t size) does return operator new(size), so no problem at all.

No, this is not correct. The default implementation may do this, but that is still implementation detail. What the compiler does is up to it. The fact remains, if you call operator new[] then you must handle that pointer under the requirements specified for pointers obtained from operator new[]. A user-provided overload could behave wildly differently and destroy your program. Even if it's not there today, someone could overload it tomorrow.

Upvotes: 2

Related Questions