C++ Logo

std-proposals

Advanced search

Re: Ptr proposal: looking for feedback

From: Jason McKesson <jmckesson_at_[hidden]>
Date: Thu, 16 Jul 2020 11:49:33 -0400
>From a purely structural perspective, this proposal is just not well
written. For example, the purpose of an abstract is to quickly gain an
understanding of what exactly is being proposed. But this abstract
really says nothing about what this `ptr` type is actually doing.
Consider this line:

> We present a smart pointer class that maintains maximal resource efficiency under refactor.

This feels a bit like marketing spin; it's littered with buzzwords
while lacking any useful content. My abstract for `unique_ptr`, for
example, would start off with "A type that wraps a pointer in a
uniquely-owning RAII-scoped object which allows one to gain reasonable
assurance that any allocated memory is cleaned up, while still
allowing the object to be moved around."

Figuring out the equivalent for your `ptr` type requires parsing
through a bunch of text and code in your proposal, when it ought to be
the first thing we see.

The key aspect of your proposal, from what I can tell, is that while
multiple `ptr` instances can reference a pointer, only one of these
objects *owns it*. So really, it's a copyable `unqiue_ptr`, where the
copies don't own the pointer. That's what your abstract needs to
communicate.

I also find the object to be lacking in useful features (to the extent
that the `ptr` type is useful). For example, if you have an allocated
object, you can't put it inside a `ptr` and have the `ptr` adopt
ownership of it. Apparently, the only way to claim ownership of such
an object is to use `make_ptr` to construct the pointer. That's not
very usable.

At the *very* least, `ptr<T>` should be constructible/assignable from
an rvalue-reference to a `unique_ptr<T, std::default_deleter>`. This
represents a transfer of ownership from the `unique_ptr` to the `ptr`.
But even then, you should have some other constructors and functions
that allow you to just take a `T*` that will be owned.

Now, we get to the question: is this a thing worth doing?

I'm going to have to come down on a hard "no."

You make reference to `shared_ptr`, but the whole point of a
`shared_ptr` is that *ownership* is shared. It's not merely that you
can reference a pointer from multiple locations in your code; it's
that any code that has such a pointer maintains ownership of it.
Dangling pointers to the object are impossible so long as all of your
pointers are shared.

Your `ptr` type is basically made for dangling pointers.

At a type level, I find `ptr` to be incoherent. Allow me to explain by analogy.

`unique_ptr` as a type represents unique ownership of an object. If a
function takes a `unique_ptr` by value or rvalue-reference, then you
know that the function is adopting ownership of some resource. The API
is clear.

`shared_ptr` as a type represents shared ownership of an object. If a
function takes a `shared_ptr` by value or rvalue-reference, then you
know that the function is adopting ownership of some resource. The API
is clear.

What does it mean for a function to take one of these `ptr` types by
value/rvalue-reference? Is the function adopting ownership of the
resource or not? That all depends on the value it is given. That's a
real problem for tracking down bugs surrounding dangling pointers. If
all of the types are the same, but the *values* in those types mean
different things, then it's hard to tell from *the type alone* who
owns a thing and who does not.

We already have a type that represents a potentially owning pointer in
C++: a *pointer*. Whether it is owned or not by the code that uses it
is based on following the chain of value assignments through various
code.

The problem here is your single-type approach. If you want one object
that represents unique ownership of a resource, that ownership should
be codified by a type. And if you want that thing to be able to hand
out non-owning reference objects, those non-owning objects should also
be codified in their own types. So there should be two types.

The thing is... we already have those two types: `unique_ptr<T>` and
`T*`. If you absolutely must avoid naked pointers, the library
fundamentals has a proposal for `observer_ptr<T>`, which is explicitly
a non-owning "smart" pointer. It's not in the standard (yet?), but you
can write one easily enough.

So we don't really need such a type. Plus, `unique_ptr` is *much* more
capable than your type as written.

Received on 2020-07-16 10:53:00