Boost logo

Boost :

Subject: [boost] Is there any interest in non-owning pointer-like types?
From: Joseph Thomson (joseph.thomson_at_[hidden])
Date: 2017-02-01 02:47:54


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 proposal
here instead.

The `observer_ptr<T>` class template is a pointer-like type that does not
do any resource management, and is intended to be used in place of `T*`
wherever `T*` is used as a non-owning reference to an object of type `T`.
`observer_ptr<T>` has three main advantages over `T*`:

   1. `T&` is implicitly convertible to `observer_ptr<T>` which, unlike `T*`,
   makes it *type-safe*. `T*` can represent things that are not
   conceptually objects of type `T`: arrays, strings, iterators. No
   implicit conversion from `T*` means that these things cannot implicitly
   convert to `observer_ptr<T>`. Pointers aren't even required to point to
   valid objects (e.g. a past-the-end iterator). Conversely, in a well-formed
   program, `T&` is *always* a valid object of type `T`.
   2. `observer_ptr<T>` documents its purpose. This is really a side-effect
   of it being type-safe (a type should have a single, specific purpose), but
   it's worth mentioning. Conversely, when you see `T*`, it may not be
   immediately obvious what it represents.
   3. `observer_ptr<T>` has a minimal interface, which makes it harder to
   misuse than `T*`; for example, `observer_ptr<T>` has no pointer
   arithmetic operators, no array subscript operator, no conversion to
   `void*`.

The `observer<T>` class template is a counterpart to `observer_ptr<T>` that
has *no null state*; it cannot be default constructed, constructed from
`nullptr_t` or constructed from `T*`, and it does not contextually convert
to `bool`. The only way to create an `observer<T>` is from `T&`. This
allows a "not null" precondition to be enforced at compile-time, rather
than having to worry about pointers being null at run-time.

Just to give you an idea of the current syntax, here is a simple tree node
type (where the nodes do not own their children) implemented using
`observer_ptr` and `observer`:

class node
{
public:
  node() = default;
  node(node const&) = delete;
  node& operator=(node const&) = delete;
  node(node&&) = delete;
  node& operator=(node&&) = delete;

  void set_parent(observer_ptr<node> new_parent) {
    if (parent) parent->remove_child(*this);
    parent = new_parent;
    if (parent) parent->add_child(*this);
  }

  observer_ptr<node> get_parent() const {
    return parent;
  }

  size_t get_child_count() const {
    return children.size();
  }

  observer<node> get_child(size_t index) const {
    return children[index];
  }

private:
  observer_ptr<node> parent;
  vector<observer<node>> children;

  void add_child(observer<node> child) {
    children.push_back(child);
  }
  void remove_child(observer<node> child) {
    children.erase(find(begin(children), end(children), child));
  }
};

And a contrived usage example:

node a, b, c;

b.set_parent(a); // conversion from `T&`
c.set_parent(b);

observer_ptr<node> x = c.get_parent();

if (x) // conversion to `bool`
{
  observer<node> y = x->get_child(0);

  assert(y == c); // comparison with `T&`

  y->set_parent(a);
  b.set_parent(y); // conversion from `observer<T>`
                       to `observer_ptr<T>`
}

A project with a working implementation
<https://github.com/hpesoj/cpp-observer/blob/master/api/observer.hpp> and
full test suite
<https://github.com/hpesoj/cpp-observer/blob/master/tests/observer_tests.cpp>
can be found here <https://github.com/hpesoj/cpp-observer>.

Regards,

Joseph


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk