我可以定义一个特定于类的释放函数并在常量表达式中使用它吗?
例如破坏性删除:
#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 和 MSVC 确实接受它,但 Clang 抱怨:
error: constexpr function never produces a constant expression [-Winvalid-constexpr]
note: call to class-specific 'operator delete'
在线演示:https://godbolt.org/z/47h1Y4sr3
哪种行为是正确的?
[expr.const]/5,要点 18-19 指出常量求值可能不会遇到:
- new 表达式 ([expr.new]),除非选定的分配函数是可替换的全局分配函数 ([new.delete.single]、[new.delete.array]) 并且分配的存储空间在计算期间被释放E;
- 删除表达式 ([expr.delete]),除非它释放在 E 的求值中分配的存储区域;
这并不指定所选的 deallocation 函数必须是可替换的全局释放函数;只要它最终调用这样的释放函数(如果需要满足前面的要点),您的代码就是有效的。
进一步可能激励的示例:
#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);
#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);
也就是说,删除表达式释放分配的存储区域是否重要,或者是否期望它是与删除表达式的操作数指向的对象相对应的存储区域?也许当前的语言应该被它的倒装所取代:删除表达式 ([expr.delete]),
unless it
- which
释放存储区域not 在 E 的计算中分配;