在不完整类型的空指针上调用delete是否合法?

Dan*_* M. 18 c++ nullptr language-lawyer delete-operator incomplete-type

如果是这样,为什么下面的代码会给我警告

注意:即使在定义类时声明析构函数也不会调用析构函数或特定于类的运算符delete

struct C;

int main()
{
    C *c = nullptr;

    delete c;

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

明白为什么它在一般情况下是不确定的行为,如果C有非平凡/虚析构函数,但不标准的保证/定义deletenullptr始终是一个noop不管情况如何?

重申:我具体询问指向不完整类型的指针的情况nullptr!

Nic*_*las 16

标准说([expr.delete]/5):

如果被删除的对象在删除时具有不完整的类类型,并且完整的类具有非平凡的析构函数或释放函数,则行为是未定义的.

因此,如果T有一个非平凡的析构函数或有一个operator delete过载,你得到UB.关于UB基于指针的(即:它是否为空指针)没有任何说法.


什么"被删除的对象"是什么意思?

可以认为"删除对象"意味着该子句仅适用于delete对实际对象的调用.因此,如果传递空指针,则不适用.

首先,关于delete显式行为的其余标准讨论调用了它的行为不适用于空指针.[expr.delete]/6&7都以"如果delete-expression的操作数的值不是空指针值"开头.第5段明确地不包含这些词.因此,我们必须假设它确实适用于空指针.

第二,如果传递空指针,"被删除对象"的含义是什么?毕竟,那里没有"对象".

好吧,如果"被删除的对象"具体谈到该指针末尾的对象,请考虑解释此文本意味着什么.那么,如果您使用非平凡的析构函数删除一组不完整的类,会发生什么?

根据该逻辑,该子句不适用,无论指针是否为空.为什么?因为"被删除的对象"是数组类型,而不是类类型.因此,本条款不适用.这意味着编译器必须能够delete[]在不完整的类数组上调用.

但这是不可能实现的; 它需要编译器能够跟踪尚不存在的代码.

所以,无论是"被删除的对象"是std::remove_pointer_t<std::decay_t<decltype(expr)>>,或标准要求的行为是不可能实现的.标准的措辞可能会被清理一下,取而代之的是"如果正在删除的对象在删除时具有不完整的类类型",则"如果T是指针U或数组U,并且U在删除时具有不完整的类类型" ,......"

  • 但是如果指针的值为null,那么就没有对象.所以这不适用. (5认同)
  • @rex从技术角度来看,标准宣称为UB的许多事情似乎是直接定义的行为.`((Foo*)nullptr) - > someStaticVariable`例如.没有正确思想的编译器实际上会发出代码来取消引用空指针.不过,声明是UB.而事实上,它被声明为UB意味着你必须期望一些(未来)编译器只是丢弃你的UB调用代码作为优化. (3认同)
  • 如果对象的生命周期没有开始,它是一个对象吗?我可能在这里错了,我看到标准的其余部分确实将"空指针值"称为其余语句中的特定限定符.最后,似乎UB的决定因素不是指针的值,而是删除操作符是否有任何操作. (3认同)
  • "被删除的对象"是指什么?`delete`语句本身的操作数,或`delete`语句的操作数指向的对象?(正版问题,我没有标准.)如果是前者,这个答案是有道理的.如果后者,操作数为空指针,则"被删除的对象"缺少指称对象,就像"现在的法国国王",并且该陈述根本不适用.在这种情况下,"它会为空指针做出规定"是无关紧要的; 隐式地,它**,因为空指针永远不会指向一个对象. (3认同)
  • 请记住,总是有第3个选项:标准在这里有缺陷,其字面词不能准确地传达其意图.这不是第一次.我认为,只要合理的人能够开始就哪种解释最好的问题开始争论就是这种情况.:-P (2认同)