Date: Sat, 18 Jul 2020 19:32:28 -0400
On Sat, Jul 18, 2020 at 5:11 PM Jefferson Carpenter via Std-Proposals <
std-proposals_at_[hidden]> wrote:
> On 7/18/2020 8:18 PM, Jason McKesson via Std-Proposals wrote:
> >
> > You may have missed a crucial part of the example. It was specifically
> > about a function that *stores* the pointer in an object (perhaps the
> > function is that object's constructor). The expectation in the
> > `unique_ptr` case is that the object now owns that resource. The
> > expectation in the `ptr` case is... what? Maybe the object owns it,
> > maybe it doesn't? So how do you now test the behavior of the object
> > after this function's execution?
> >
> > After all, the object stored that `ptr` so that it can use it. Which
> > functions on that object are expected to be valid in the event of a
> > dangling pointer and which aren't?
> >
> > If you're in the realm of pure functions with immutable state, maybe
> > this type makes sense. But in the real world, where state is changed
> > frequently and resources are owned by more than just stack variables,
> > this just isn't tenable as a general-purpose tool.
> >
>
> Ah, I think you're asking what happens with aliasing. As a trivial
> example that bypasses even object storage
>
> template<typename T>
> ptr<T> identity(ptr<T> t) {
> return t;
> }
>
Ooh, `return t` — now you're really getting into my home turf! :D
Just to absolutely drive home why "copy-construct" and "move-construct"
need to be considered part of the same overload set, let's look at how GCC
and Clang compile this sort of implicitly-moving code. This "Fowl / eleven"
example comes straight from my paper P1155 More Implicit Move
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1155r3.html>. As
of C++20, GCC's behavior is "correct" and Clang's behavior is "incorrect";
but in C++17 the opposite was true, and similar examples can always be
constructed just outside the bounds of what we've standardized so far.
https://godbolt.org/z/4qafK3
Notice that GCC dereferences the pointer before deleting it (that is, it
calls the move-ctor which "correctly" transfers ownership from ptr<int> to
Fowl), and Clang deletes the pointer before dereferencing it (that is, it
calls the copy-ctor which does not transfer ownership from ptr<int> to
Fowl).
This is the kind of pitfall that I do not want in a library facility.
[...]
> If it's desirable not to have to know this, then it might be good to
> have a non-thread-safe refcount. I'll think about the consequences of
> this and try to work it into the next revision of the document.
>
Let's be very clear that what you have today *is **not refcounted*. A
"reference count" is a count associated with the *controlled object*, not
with the pointer(s) pointing to it. Two different pointers pointing to the
same object should never disagree about the refcount on that object.
You can do internal refcounting (like P0468
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0468r1.html>'s
proposed `retain_ptr`) or external, "control-block"-based refcounting (like
`std::shared_ptr` and `boost::local_shared_ptr`), but you can't hold a
refcount inside the pointer object itself. That's not a refcount, because
it can't be made to go up when someone else takes a reference, nor go down
when someone else releases a reference.
If you do want a non-thread-safe shared_ptr, `local_shared_ptr` is in Boost
(and has already been rejected for standardization).
–Arthur
std-proposals_at_[hidden]> wrote:
> On 7/18/2020 8:18 PM, Jason McKesson via Std-Proposals wrote:
> >
> > You may have missed a crucial part of the example. It was specifically
> > about a function that *stores* the pointer in an object (perhaps the
> > function is that object's constructor). The expectation in the
> > `unique_ptr` case is that the object now owns that resource. The
> > expectation in the `ptr` case is... what? Maybe the object owns it,
> > maybe it doesn't? So how do you now test the behavior of the object
> > after this function's execution?
> >
> > After all, the object stored that `ptr` so that it can use it. Which
> > functions on that object are expected to be valid in the event of a
> > dangling pointer and which aren't?
> >
> > If you're in the realm of pure functions with immutable state, maybe
> > this type makes sense. But in the real world, where state is changed
> > frequently and resources are owned by more than just stack variables,
> > this just isn't tenable as a general-purpose tool.
> >
>
> Ah, I think you're asking what happens with aliasing. As a trivial
> example that bypasses even object storage
>
> template<typename T>
> ptr<T> identity(ptr<T> t) {
> return t;
> }
>
Ooh, `return t` — now you're really getting into my home turf! :D
Just to absolutely drive home why "copy-construct" and "move-construct"
need to be considered part of the same overload set, let's look at how GCC
and Clang compile this sort of implicitly-moving code. This "Fowl / eleven"
example comes straight from my paper P1155 More Implicit Move
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1155r3.html>. As
of C++20, GCC's behavior is "correct" and Clang's behavior is "incorrect";
but in C++17 the opposite was true, and similar examples can always be
constructed just outside the bounds of what we've standardized so far.
https://godbolt.org/z/4qafK3
Notice that GCC dereferences the pointer before deleting it (that is, it
calls the move-ctor which "correctly" transfers ownership from ptr<int> to
Fowl), and Clang deletes the pointer before dereferencing it (that is, it
calls the copy-ctor which does not transfer ownership from ptr<int> to
Fowl).
This is the kind of pitfall that I do not want in a library facility.
[...]
> If it's desirable not to have to know this, then it might be good to
> have a non-thread-safe refcount. I'll think about the consequences of
> this and try to work it into the next revision of the document.
>
Let's be very clear that what you have today *is **not refcounted*. A
"reference count" is a count associated with the *controlled object*, not
with the pointer(s) pointing to it. Two different pointers pointing to the
same object should never disagree about the refcount on that object.
You can do internal refcounting (like P0468
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0468r1.html>'s
proposed `retain_ptr`) or external, "control-block"-based refcounting (like
`std::shared_ptr` and `boost::local_shared_ptr`), but you can't hold a
refcount inside the pointer object itself. That's not a refcount, because
it can't be made to go up when someone else takes a reference, nor go down
when someone else releases a reference.
If you do want a non-thread-safe shared_ptr, `local_shared_ptr` is in Boost
(and has already been rejected for standardization).
–Arthur
Received on 2020-07-18 18:35:57