Yikes! (3) being not true to me means that I needed non-local reasoning to
determine the optionality.
I reason about symmetric operations like new/delete in a similar way to
other symmetric operations like open/close. Even though close(-1) returns
EBADF and doesn't do something bad, it's surprising to the reader if
there's a naked call to close when there may or may not be a call to open.
int fd = -1;
...
if (something) { fd = open(); }
...
close(fd);
It seems clever to rely on close(-1) not doing something bad, and we're
likely to mislead the reader when they encounter the close statement
separate from the other logic. Whereas if I saw the following, I don't need
non-local reasoning to determine that the fd may not represent an open file:
int fd = -1;
...
if (something) { fd = open(); }
...
if (fd != -1) { close(fd); }
Is there something fundamentally different about new/delete?
Generally we use options for optionality, however we've occasionally
avoided Option<T*> in favor of T* out of convenience. For these Option<T*>
variables masquerading as T* variables, it would be great to keep the
optionality checks to help the reader intuit the optionality, or convert to
something better than just naked deletes.