10

Why would deleting an object through void* be undefined behavior, rather than compilation error?

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

This code compiles and produces code, albeit with the warning on gcc and clang (surprisingly, ICC doesn't give a warning):

:2:5: warning: cannot delete expression with pointer-to-'void' type 'void *' [-Wdelete-incomplete]

Why is not simply malformed program with invalid syntax? Looks like Standard doesn't spend too much time on it, saying in [expr.delete] that

This implies that an object cannot be deleted using a pointer of type void* because void is not an object type.

Would there be any reason I am missing why this does not trigger a hard compilation error?

curiousguy
  • 8,038
  • 2
  • 40
  • 58
SergeyA
  • 61,605
  • 5
  • 78
  • 137
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/173179/discussion-between-curg-and-sergeya). –  Jun 14 '18 at 21:01

1 Answers1

14

In modern C++ deleting a void * pointer is ill-formed (i.e. it is what we typically call a "compilation error")

8.3.5 Delete
1 [...] The operand shall be of pointer to object type or of class type.

void * is not a pointer to object type.

In C++98 the situation was different. Deleting a null pointer of void * type was a NOP, while deleting a non-null pointer of void * type was UB.

This change in the specification appears to have been triggered by defect report #599. The original spec allowed supplying null pointers of any pointer type in delete-expression, like function pointers, for example. This looked unnecessarily permissive. The resolution of DR#599 tightened the requirements, outlawing void * as well.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • Does it mean that neither of mighty 3 compilers are conforming? – SergeyA Jun 13 '18 at 17:57
  • 6
    @SergeyA: They are, as long as they issue a *diagnostic message*. Neither C nor C++ formally support the distinction between "warnings" and "errors". After issuing a diagnostic message (any message) for ill-formed code, the compiler is free to continue the compilation. The behavior of the resultant code is not defined by the lanaguage, though. – AnT stands with Russia Jun 13 '18 at 17:58
  • @SergeyA No, extensions are ok if they don't change the meaning of any well-formed program (http://eel.is/c++draft/intro.compliance#8) – Rakete1111 Jun 13 '18 at 17:59
  • @SergeyA [they just have to issue a diagnostic](https://stackoverflow.com/a/31685448/1708801) there are cases such a narrowing conversions that compilers choose warnings over errors. Legacy code could be one of many reasons. – Shafik Yaghmour Jun 13 '18 at 18:02
  • 1
    This just begs the follow up question: why was it UB and not ill-formed *in C++98*? – Matteo Italia Jun 13 '18 at 18:56
  • 3
    @Matteo Italia: I think DR#599 is what lead to this change. I added it to the answer. – AnT stands with Russia Jun 13 '18 at 19:15
  • Since the Standard came after the language is in use, perhaps some implementations would process a `delete` of a `void*` as though the pointer type matched that given to `new`? Making a behavior UB is a way for the Standard to invite any implementations that behave that way to keep on doing so, without mandating that others do likewise. Better, IMHO, would be to say that compilers could either regard it as a constraint violation, or process it with predictable semantics in at least some cases (e.g. objects without destructors), but the authors of the Standard seem loath to do that. – supercat Jun 14 '18 at 15:07
  • @AnT just one small point, void* could be a pointer to an object type, you just wouldn't know when looking at the line using void*. E.g. string* p = new string; void* p2 = p; Hence my previous comment about trying to avoid the use of void*. –  Jun 14 '18 at 19:31
  • @Curg: Well, yes. But I'm sure that the standard wording "shall be of pointer to object type" is intended to refer to the *static type* of the argument expression, not the dynamic type of the actual run-time pointee. – AnT stands with Russia Jun 14 '18 at 20:15
  • 1
    I would expect that some implementations probably processed `new` and `delete` in such a way that a compiler didn't have to know or care about the type of pointer passed to `delete`, and some programs may have relied upon that. Making the behavior undefined would effectively categorize such programs as being conforming but not portable. – supercat Jul 11 '18 at 22:38
  • In early C++ this was in fact well formed but still did something not so nice. It either deleted the object correctly or deleted the object without running the destructor and you didn't know which. – Joshua Mar 25 '21 at 19:32