C++中new表达式初始化抛异常时释放函数查找的异常行为问询
初始测试场景
以下代码中,S的构造函数抛出异常,触发new表达式的释放逻辑:
#include <new> #include <iostream> #include <cstdlib> struct alignas(32) S { S() {throw 1;} void* operator new(std::size_t count, std::align_val_t al) { return ::operator new(count, al); } void operator delete(void* ptr, std::align_val_t al) { std::cerr << "aligned delete\n"; ::operator delete(ptr, al); } }; int main() { try { S* ps = new S; } catch(...) { } }
运行后输出aligned delete。
为S添加模板化delete函数后:
struct alignas(32) S { // 原有成员省略 template <typename... Args> void operator delete(void* ptr, Args... args) { std::cerr << "templated delete\n"; } // 原有成员省略 };
运行输出仍为aligned delete;只有移除aligned delete函数后,才会输出templated delete。
标准规则与疑问
对照C++标准[expr.new#28]规则:
A declaration of a placement deallocation function matches the declaration of a placement allocation function if it has the same number of parameters and, after parameter transformations ([dcl.fct]), all parameter types except the first are identical. If the lookup finds a single matching deallocation function, that function will be called; otherwise, no deallocation function will be called. If the lookup finds a usual deallocation function and that function, considered as a placement deallocation function, would have been selected as a match for the allocation function, the program is ill-formed. For a non-placement allocation function, the normal deallocation function lookup is used to find the matching deallocation function ([expr.delete]).
存在以下疑问:
- 标准未明确定义placement allocation function,是否指参数数量≥2的分配函数?
- 当存在aligned delete与模板化delete两个匹配的释放函数时,为何未触发「无释放函数调用」的规则?
- aligned delete属于常规释放函数,且匹配分配函数,按规则程序应非法,但实际可正常运行?
后续测试结果
- 仅保留单参数delete时:
void operator delete(void* ptr) { std::cerr << "single-parameter delete\n"; ::operator delete(ptr); }
运行无输出且无警告,说明编译器将aligned new视为placement allocation function,与标准规则相悖。
将分配函数替换为单参数new时,行为符合预期:仅存在aligned delete或搭配模板化delete时输出
aligned delete,仅保留模板化delete时无输出且有警告。destroying delete未被优先调用:
struct S { S() { throw 1; } void operator delete(S* ptr, std::destroying_delete_t) { std::cerr << "destroying delete\n"; ptr->~S(); ::operator delete(ptr); } void operator delete(void* ptr) { std::cerr << "single-parameter delete\n"; ::operator delete(ptr); } };
运行输出single-parameter delete,但按[expr.delete]规则destroying delete应具有最高优先级,不过对象未构造完成是否不应考虑该函数?
核心疑问
上述测试行为是编译器不符合标准,还是对标准的理解存在偏差?
内容的提问来源于stack exchange,提问作者Blackteahamburger

