c++ - 私有运算符 delete 会触发 GCC 和 Clang 的编译时错误,但不会触发 MSVC
问题描述
受这个不太好问的重复的启发,我相信这个问题值得一个新的、独立的、标题明确的问题。以下代码会触发 GCC 8.1.0 和 Clang 6.0.0 的编译错误,但不会触发 MSVC 19.00:
class X {
public:
X() /* noexcept */ { }
private:
static void operator delete(void*) { }
};
int main() {
X* x = new X{};
}
从expr.new:
如果上述对象初始化的任何部分通过抛出异常而终止并且可以找到合适的释放函数,则调用释放函数以释放构造对象的内存,之后异常继续在上下文中传播的新表达式。如果找不到明确匹配的解除分配函数,则传播异常不会导致对象的内存被释放。[ <em>注意:这适用于被调用的分配函数不分配内存的情况;否则,很可能导致内存泄漏。— <em>结束注释]
事实上,这并不意味着如果::operator delete
找不到匹配的释放函数就应该触发编译错误。或者,是否将其设为私有只会导致可以找到但无法访问的结果?哪些编译器是正确的?
解决方案
这里有两个问题:
operator delete
即使它是私人的,如何找到?
C++ 首先尝试在任何地方查找名称;检查访问保护是一个后期阶段。
因此,您operator delete
已找到,但无法访问。
operator delete
当构造函数是时,为什么我必须是可访问的noexcept
?
措辞“如果对象初始化的任何部分 [...] 通过抛出异常而终止”表明该段落的其余部分不适用,因为noexcept
.
但是,正如“...的任何部分”所建议的那样,在分配和进入构造函数之间(在评估初始化器时)或退出构造器之后(在销毁初始化器时)可能会有异常。
考虑
struct Y
{
Y() {}
Y(const Y&) { throw "sorry"; }
};
class X {
public:
X(Y y) noexcept { }
private:
static void operator delete(void*) { }
};
int main() {
Y y;
X* x = new X{y};
}
Y
复制构造函数在进入构造函数之前抛出,X
但在分配之后,因此需要释放内存。
所以我认为 Visual C++ 是错误的(再次)。
推荐阅读
- metamask - 我想知道有什么方法可以像元数据一样创建自定义扩展
- c# - TreeView 所有者绘制异常
- node.js - 为 CRUD 实现扩展基类时出错
- node.js - 节点快递中的静态文件路由不起作用
- amazon-web-services - 我们可以在没有 AWS Lambda 服务的情况下使用 AWS lex 构建 Bot
- elasticsearch - 调用弹性搜索时如何选择首选数据中心?
- javascript - PEG.js 允许两个标签之间的任何文本
- javascript - 通过单击关闭图标从表中删除行
- javascript - 如何使用纯 Javascript Autodesk 在查看器中离线显示 2d (.dwg) 文件
- javascript - 如何使用 @ 登录键 javascript 获取键的值