On Thu, Feb 2, 2017 at 6:18 AM, Niall Douglas
wrote: For some time, I have been developing a pair of non-owning pointer-like types that I am currently calling `observer_ptr` and `observer`. I had planned to propose their addition to the C++ standard library, but I have been informed by the author of the original `observer_ptr` proposal http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4282.pdf that the ISO C++ committee has rejected his proposal and made clear that it feels there is no place in the standard library for such types, believing that this role is filled to a satisfactory degree by regular pointers. I wholeheartedly disagree with this assessment, so I am bringing my
here instead. I don't think that exactly accurate. Rather it is that the GSL's
On 01/02/2017 07:47, Joseph Thomson wrote: proposal proposed schema of:
* owner<T> is an owning pointer * span<T> is a borrowed pointer/reference/view * C type pointers are non-owning, non-borrowed
As far as I understand it, `span<T>` is a view of objects over a range, in the vein of `array_view<T>`, and raw pointers are simply non-owning. I'm not sure what "borrowed" means in this context.
... has general wide agreement in principle, if not in exact
formulation. The Library Fundamentals TS v2 does have an observer_ptr in std::experimental, but there is general lack of sureness as to what it actually contributes when a non-annotated pointer according to the GSL schema is either an observer by definition, or unupgraded code.
That is just one of the benefits I listed (documentation of intent). The most benefit important IMO is *type-safety*, followed closely by a more minimal interface. The `observer_ptr<T>` in `std::experimental` only has type safety in one direction (conversion to `T*` is explicit), because the only way to construct it is from `T*`. Observers should preferably be constructed from `T&`, as this is a type-safe conversion. The `observer_ptr<T>` in `std::experimental` needs one change to make it type safe: change `make_observer(T*)` to `make_observer(T&)`. We need a proposal to change experimental::observer_ptr :)
In my opinion, designating `T*` to mean "observer" would be okay in an ideal world (it still has far too general an interface for something that just observes), but the world isn't ideal. Not everyone reads the GSL guidelines, and those who do won't follow it to the letter, and even if they did there is still plenty of "unupgraded" code. Also, I think you too easily dismiss the benefit of being able to distinguish between un-upgraded and upgraded code; this sounds very useful if you ask me. So, to reiterate again, `observer_ptr`:
- Allows upgraded code to be distinguished from un-upgraded code (this is more important than you might think) - Removes operations that are inappropriate for an observer type (pointer arithmetic, array subscript, conversion to `void*`) - Provides type safety by allowing construction from `T&` and disallowing construction from and conversion to `T*` I agree we need this vocabulary type. GSL guidelines are for code that will be written from scratch with C++14/C++17. The reality is that we have tons of C++98 code. Pretending that T* is a not owning pointer because we have gsl::owner is not useful in a legacy code project. I'm not saying that gsl::owner is not useful. Whether to allow implicit/explicit conversion from `T&` is a design detail. The benefits are there regardless. Yes, I believe the implicit conversion could be questionable.
Also, it seems to me that the natural meaning of `T*` in C++ is as an iterator, not an observer:
int arr[] = { 1, 2, 3 }; auto it = std::end(arr); // `decltype(it)` is `int*`
I'd need a fair bit of convincing that observer<T> has merit in any form
except additional clarity to help demarcate unupgraded code from upgraded code. If that's your argument, and you're not doing funny things with implicit conversion from T& and other funny non-GSL semantics, I'd suppose this though I'd suggest you actually contribute it to GSL itself and persuade Neil to let it in.
`observer<T>` is similar to `not_null<T>`, in that it enforces a "not null" precondition, except `observer<T>` does a much better job because it enforces the precondition at compile-time.
auto o = make_observer(null_ptr); // compile error: expects `T&` auto n = not_null
(null_ptr); // compiles fine; run-time error I prefer also a not null smart pointer that takes this as a
Le 02/02/2017 à 02:15, Joseph Thomson a écrit : precondition. Requiring the class to check the parameter is not nullptr (as gsl::not_null does) is more than what I need.
`observer` forces the user to dereference their pointer, which should be flagged as unsafe by the static analyser if they don't check for null. `not_null` waits until compile-time to report the error, and makes it harder for a static analyser to catch the potential null pointer bug.
I have had a few discussions with Neil over at the GSL GitHub page about the purpose of `not_null`. Honestly, the design goals of `not_null<T>` seem unclear to me; it seems to be trying to do too many things at once, simultaneously trying to be a drop-in replacement for `T*` that enforces a "not null" precondition at run-time (it's meant to be able to hold any kind of pointer or pointer-like object), but also trying to be this high-level modern type that has compile-time type safety (it deletes various pointer arithmetic operations, and it has been seriously suggested that conversion from `T` should be explicit). It was initially meant to be compatible with smart pointers like `unique_ptr`, but this goal seems to have been all but abandoned after it was realised that this won't really work. It's also meant to be used in conjunction with strings, despite the fact that `string_view` is a much better alternative, and with the `owner` annotation, despite the fact that this will probably have the same conceptual problems as `unique_ptr`. If my ramblings sound confused, it's because I am confused, because `not_null` is confused. Maybe we could have not_null<Ptr> that works for any pointer-like type, but what I need today is not_null_ptr<T>. Maybe it is worth providing a single not_null
instantiation. The GSL is a *much* better home for it than Boost because then you'll
have Bjarne batting for it, plus static checking support from Microsoft in VS2017 and Google via clang-tidy. You'll also get a *huge* userbase almost instantly, because the GSL or rather one of its C++ 98 clones is seeing exponential growth recently. It's amazingly useful for upgrading ancient C++ codebases.
This would be good. If I brought this to the GSL, I would pretty much be proposing that `not_null` is scrapped in favour of `observer` and `observer_ptr`. `not_null<zstring>` is obsoleted by `string_view`; `not_null
` etc. are broken; `not_null ` is of little value IMO, and may be conceptually broken too; honestly, the only useful case is `not_null `, and that currently has big issues with its design too. `observer` and `observer_ptr` are essentially `maybe_null` and `not_null` with clearer design goals and a fixed interface.
In reverse order ;-) Vicente