On Fri, Feb 3, 2017 at 1:17 PM, David Stone wrote:
There is only one proper way to represent the concept of a T that might not
be a T: optional<T>, so I think you are defining your objects backward. The
"top level" type should be observer<T>, and if you want to support the
concept of nullness, use optional. This is a much more
consistent way to treat nullness throughout the language, rather than
special-casing it for every type.
I actually agree with you from an ideological perspective, but practical
implications drove me to my current design.
My original goal was to create a better interface for `not_null`, where
the "not null" precondition was enforced at compile-time. I came up with
`observer<T>` and deligated `optional` as the optional
counterpart. However, I realised that `optional` is not a
zero-overhead abstraction: it is twice the size of `T*` and many of its
operations do more than just reading and writing a pointer. I worried that
this would hurt adoption, so I created a zero-overhead replacement called
`optional_observer<T>`.
Originally, I was using `nullopt` to make `optional_observer<T>` look like
`optional<T>`, but soon realised that it was impossible to get both
zero-overhead comparison operations *and* have behavour consistent with
`optional<T>` (essentially, `nullopt` compares lower than any `T`, but the
ordering of `nullptr_t` is implementation-specified). Thus, I replaced it
with my own `nullobs` constant before realising it was just duplicating the
function of `nullptr` (`optional_observer<T>` was constructible from `T*`
and `nullptr_t` for performance and convenience reasons), so I removed
`nullobs` and ended up with something that looked very much like
`observer_ptr<T>`, so I renamed it and here we are today.
And `observer<T>` is still the "top level" type in a sense. `make_observer`
returns an `observer<T>`, and `observer_ptr<T>` is implicitly convertible
from `observer<T>`. This is another thing that is wrong with
`not_null`: it gets things backwards by putting the nullable type
(`T*`) on top and trying to suppress that nullability with a wrapper. This
is why it ends enforcing the "not null" precondition at run-time instead of
compile-time, because `not_null<T>` has to be constructed from `T`, a
nullable type.