为什么删除void*会导致UB而不是编译错误?

10

为什么通过 void* 删除对象会导致未定义的行为,而不是编译错误?

void foo(void* p) {
    delete p;
}

这段代码可以编译并生成代码,尽管在gcc和clang上会出现警告(令人惊讶的是,ICC没有警告):

:2:5: 警告: 无法使用指向“void”类型的指针表达式删除 'void *' [-Wdelete-incomplete]

为什么它不仅仅是语法无效的拼写错误呢?看起来标准对此没有花费太多时间,在[expr.delete]中说:

这意味着不能使用类型为void*的指针删除对象,因为void不是一个对象类型。

是否有任何我忽略的原因,使得这不会触发严格的编译错误?


让我们在聊天中继续这个讨论 - user2827968
1个回答

14

在现代C++中,删除 void * 指针是不被允许的(即我们通常所说的“编译错误”)

8.3.5 删除
1 [...] 操作数必须是对象指针类型或类类型。

void * 不是指向对象类型的指针。

在C ++98中,情况不同。删除 void * 类型的空指针是一个NOP,而删除非空 void * 类型的指针是UB。

这种规范上的变化似乎是由 缺陷报告# 599 引发的。最初的规范允许在 delete-expression 中提供任何指针类型的空指针,例如函数指针。这显得过于宽容。DR#599 的解决方案加强了要求,禁止使用 void *


6
只要它们发出一条“诊断消息”,它们就是有效的。 C和C ++都没有正式支持“警告”和“错误”之间的区别。在为有问题的代码发出诊断消息后,编译器可以自由地继续编译。但所得到的代码的行为在语言中没有被定义。 - AnT stands with Russia
1
这只是引出了一个后续问题:为什么它在C++98中是UB而不是格式错误? - Matteo Italia
3
@Matteo Italia:我认为DR#599是导致这种变化的原因。我将其添加到答案中。 - AnT stands with Russia
1
我认为一些实现可能会以某种方式处理newdelete,使得编译器不必知道或关心传递给delete的指针类型,而有些程序可能依赖于此。将行为定义为未定义将有效地将这些程序归类为符合但不可移植的程序。 - supercat
在早期的C++中,这个语句实际上是正确形式,但仍然存在一个不太好的问题。它要么正确地删除了对象,要么在不运行析构函数的情况下删除了对象,你无法确定哪种情况发生了。 - Joshua
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接