Can I define one of class-specific deallocation functions and use it in a constant expression?
For example a destroying delete:
#include <new>
struct A {
constexpr void operator delete(A *, std::destroying_delete_t);
};
struct B : A {};
constexpr void A::operator delete(A *p, std::destroying_delete_t) {
::delete static_cast<B*>(p);
}
constexpr bool foo() {
A *p = new B;
delete p;
return true;
}
static_assert( foo() );
GCC and MSVC do accept it, however Clang complains:
error: constexpr function never produces a constant expression [-Winvalid-constexpr]
note: call to class-specific 'operator delete'
Online demo: https://godbolt.org/z/47h1Y4sr3
Which behavior is correct?
[expr.const]/5, bullet points 18-19 state that constant evaluation may not encounter:
- a new-expression ([expr.new]), unless the selected allocation function is a replaceable global allocation function ([new.delete.single], [new.delete.array]) and the allocated storage is deallocated within the evaluation of E;
- a delete-expression ([expr.delete]), unless it deallocates a region of storage allocated within the evaluation of E;
This does not specify that the selected deallocation function must be a replaceable global deallocation function; as long as it ends up calling such a deallocation function if necessary to satisfy the preceding bullet point, your code is valid.
A further possibly-motivating example:
#include <new>
struct A {
int& r;
constexpr void operator delete(A* p, std::destroying_delete_t) {
p->r = 1;
::delete(p);
}
};
constexpr int foo() {
int i = 0;
delete new A{i};
return i;
}
static_assert(foo() == 1);
An interesting question is raised by the following:
#include <new>
struct B {
int** q;
constexpr void operator delete(B* p, std::destroying_delete_t) {
**p->q = 1;
delete p->q;
}
};
constexpr int bar() {
int j = 0;
B b{new int*(&j)};
delete &b;
return j;
}
static_assert(bar() == 1);
That is, does it matter which region of storage the delete-expression deallocates, or is it expected to be the region of storage corresponding to the object to which the operand of the delete-expression points? Perhaps the current language should be replaced by its inversion:
- a delete-expression ([expr.delete]),
unless itwhich deallocates a region of storage not allocated within the evaluation of E;