
On Fri, Feb 3, 2017 at 1:17 PM, David Stone <david@doublewise.net> 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<observer<T>>. 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<T*>`, where the "not null" precondition was enforced at compile-time. I came up with `observer<T>` and deligated `optional<observer<T>>` as the optional counterpart. However, I realised that `optional<observer<T>>` 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<T*>`: 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.