It seems like many people miss the point and the utility of this idea at first.
Consider:
template<typename Mutex>
class unique_lock
{
Mutex* pm;
public:
unique_lock() : pm() { }
unique_lock(Mutex& m) : pm(&m) { }
~unique_lock() { if (pm) pm->unlock(); }
unique_lock(unique_lock&& ul) : pm(ul.pm) { ul.pm = nullptr; }
unique_lock& operator=(unique_lock&& ul)
{
unique_lock(std::move(ul)).swap(*this);
return *this;
}
void swap(unique_lock& ul) { std::swap(pm, ul.pm); }
};
With a "dumb" smart pointer that is null-on-default-construction and null-after-move you can default three of the special member functions, so it becomes:
template<typename Mutex>
class unique_lock
{
tidy_ptr<Mutex> pm;
public:
unique_lock() = default; // 1
unique_lock(Mutex& m) : pm(&m) { }
~unique_lock() { if (pm) pm->unlock(); }
unique_lock(unique_lock&& ul) = default; // 2
unique_lock& operator=(unique_lock&& ul) = default; // 3
void swap(unique_lock& ul) { std::swap(pm, ul.pm); }
};
That's why it's useful to have a dumb, non-owning smart pointer that is null-after-move, like tidy_ptr
But observer_ptr
is only null-on-default-construction, so if it is standardized it will be useful for declaring a function to take a non-owning pointer, but it won't be useful for classes like the one above, so I'll still need another non-owning dumb pointer type. Having two non-owning dumb smart pointer types seems almost worse than having none!