3

I've been thinking that while I understand the goals of std::observer_ptr I think it would be nice if there was at least an option for a similar pointer type that knows if what it points to has been deleted. For example, we could have something like the following

slightly_smart_ptr<Foo> p1(new Foo());
auto p2 = p1;
p1.erase(); // This deletes the foo referred to by p1.
if (p2.expired())
   std::cout << "p2 is expired\n"; // this will fire

One way to achieve this with the current standard library is to make a shared_ptr to A in some scope that will exist for the lifetime of A, always refer to A by passing weak_ptrs around, and delete A when it is no longer needed by resetting the shared_ptr. The weak_ptrs here will have the basic semantics of observer_ptrs that know if A has been deleted. But there are problems with this approach: weak_ptrs must be locked, turning them into shared_ptrs to be used, which feels untidy, but more seriously a shared_ptr to A must exist somewhere, when all the user wants is a slightly smart pointer that does not own any content. The user agrees to manually destroy the content when it is time: no ownership is shared so it is a code smell for the user to create a shared_ptr in such a situation.

I however cannot think of a way in which the details of this implementation could be effectively hidden.

Also does such a pointer exist as a proposal or in a boost library or elsewhere?

jwezorek
  • 3,125
  • 1
  • 19
  • 30
  • I'm not sure I understand the question. Do you want your custom pointer to get notified when an object it points to is destroyed, regardless of how it was allocated? – HolyBlackCat Aug 16 '19 at 16:53
  • i want a custom pointer type that if p1 and p2 are instances of thetype both pointing to A, if I p1.reset(nullptr) this deletes A -- this operation does not need to be called reset but you get the idea -- then p2.expired() will be true. – jwezorek Aug 16 '19 at 16:56
  • Sounds a lot like Qt's [QPointer](https://doc.qt.io/qt-5/qpointer.html). – Jesper Juhl Aug 16 '19 at 16:59
  • 1
    Tricky. No small part of why weak pointer requires the lock is so that the allocation doesn't get freed part way through using the allocation. If you only want to know "Is the pointer at this exact point in time valid?" then locking it is excessive, but I don't see much point to the alternative. The allocation might be gone immediately after the test. Are you after a notification on deallocation? – user4581301 Aug 16 '19 at 16:59
  • 7
    I don't really see the issue with using `shared_ptr` and `weak_ptr`. No matter how you design this you need to somehow lock the object so one pointer does not delete it while another is using it. – super Aug 16 '19 at 17:00
  • "if you only want to know "Is the pointer at this exact point in time valid?" then locking it is excessive, but I don't see much point to the alternative." --- I mean without locking it would just mean that slightly_smart_ptr would not be thread-safe, right? But one of the big criticisms of shared_ptr is that you are paying for thread safety even if you do not require it. – jwezorek Aug 16 '19 at 17:44
  • 1
    @jwezorek no just that, but `if(ptr.valid()) { stuff(); ptr->a; }` The `stuff()` function can invalidate the pointer, so you'd have to check before every statements – Guillaume Racicot Aug 16 '19 at 18:19
  • What you are asking for is hard-to-impossible in the general case, but without threads (and assuming you aren't playing much with interrupts) you have just restricted the use cases to eliminate the majority of the truly thorny problems. I recommend adding that to the question. – user4581301 Aug 16 '19 at 18:34
  • Rethinking that a bit. There are excellent answers to this question based on the question as written. You're better off writing a new question rather than invalidating them. – user4581301 Aug 16 '19 at 18:40

2 Answers2

6

Not feasible in general.

The entire purpose of the extant smart pointers is to keep track of object lifetime and ownership in a way that simply isn't possible in general with raw pointers, unless you hooked into the allocator and had some convoluted relationship between this allocator and any handles pertaining to the allocated object.

The benefits you are describing are the benefits that come neatly from using said extant smart pointers. shared_ptr and weak_ptr are perfect here.

There's no problem with locking (you want this) and there's no problem with there having to be a shared_ptr somewhere, because surely someone somewhere does own that data. If they don't, your design has much bigger problems and you're trying to hack around those problems with a similarly broken smart pointer concept that'll never exist in the standard.

Lightness Races in Orbit
  • 358,771
  • 68
  • 593
  • 989
6

The problem of such smart pointer is that it would be more error prone than std::unique_ptr, T* or std::weak_ptr.

When you want to know if a pointer has been deleted from elsewhere by it's unique owner, in reality you need shared ownership and std::weak_ptr.

You see, there is a reason why you need to "lock" a weak pointer before using it. It's because when you start using it, you gain ownership of the pointer. If you cannot lock your "observer pointer that knows if deleted or not", you cannot safely use it, since at any moment after verifying it's validity, it can be deleted.


Also, you have a deeper contradiction.

When you have a unique pointer, you know who is gonna delete it, and you know who is the owner.

If you have a program that checks for the validity of a pointer at runtime, then it's because your program doesn't know the state of the ownership of the resource.

If your program or parts of your program cannot know the state of the ownership of a resource and need to check if it had been deleted or not, then you need to ensure that it won't be deleted the next line while using it, since it can be deleted at any time, since you cannot know about its ownership status. Therefore you need to own the resource temporarily while using it. Therefore you need shared ownership to defer the ownership decision while executing the code.

If you have shared ownership, you don't need a observer pointer that knows if deleted or not.

Your pointer don't need to exist then.


So... you thought you need that pointer, it could be handy... what can you do?

You need to review your code. If there is a single ownership, why do you need to know the validity of the pointer. Why cannot you simply ask the owner?

If the owner don't exist, maybe your code that want to do the check should not be valid when the owner is deleted. Maybe your structure that want to do the check should die at the same time as the owner.

If your unique owner dies at an unpredictable moment (for example, your unique owner is held by a shared owner) then maybe your structure should check the validity of the shared owner instead.

Maybe your code calling the function that want to check if its pointer is still valid should simply not call it when there owner is dead.

...

And so on.

There is so many ways to solve that, but needing a weak pointer on a unique owner usually shows a flaw in the program or a problem in the reasoning of the lifetime of the objects in your program.

Guillaume Racicot
  • 32,627
  • 7
  • 60
  • 103