title: memory management for an immutable data model
body: <p>I've written a little toy data model, which uses a <code>std::set</code> to unique instances. This may be called either the flyweight pattern, or <a href="https://en.wikipedia.org/wiki/Hash_consing" rel="nofollow noreferrer">hash-consing</a> (though, technically, I'm not yet using a hash table).</p>
  
  <pre><code>#include &lt;set&gt;
  #include &lt;cassert&gt;
  
  enum Color { Red, Green, Blue };
  
  struct Shape {
  
    // Set the color, return a new shape.
    virtual const Shape* color(Color) const = 0;
  
  };
  
  template&lt;class T&gt;
  const T* make_shape(T value) {
  
    struct cmp {
      bool operator()(const T* a, const T* b) const {
        return *a &lt; *b;
      }
    };
  
    static std::set&lt;const T*, cmp &gt; values;
  
    auto iter = values.find(&amp;value);
  
    if(iter != values.end()) {
      return *iter;
    }
  
    auto p = new T(value);
    values.insert(p);
    return p;
  
  }
  
  struct Circle : public Shape {
    Color c = Red;
    virtual const Shape* color(Color c) const {
      Circle r;
      r.c = c;
      return make_shape(r);
    }
    bool operator&lt;(const Circle&amp; rhs) const { return c &lt; rhs.c; }
  };
  
  </code></pre>
  
  <p>Here's my test code. Notice how the first two lines return the same pointer, so these semantics are different from normal allocation via <code>new</code> or <code>make_shared</code>.</p>
  
  <pre><code>
  void test_shape() {
  
    auto s0 = make_shape(Circle{});
    auto s1 = make_shape(Circle{});
  
    // Structurally equivalent values yield the same pointer.
    assert(s0 == s1);
  
    // Color is red by default, so we should get the same pointer.
    auto s2 = s0-&gt;color(Red);
  
    assert(s2 == s0);
  
    // Changing to Green gives us a different pointer.
    auto s3 = s0-&gt;color(Green);
  
    assert(s3 != s0);
  
    printf("done\n");
  
  }
  
  int main() {
      test_shape();
  }
  
  </code></pre>
  
  <p>Right now, the shapes are simply leaked. That is to say that once a client of this data model no longer has a pointer to a <code>Shape</code>, that shape is not deallocated (consider the <code>set</code> to be weak references that should be broken).</p>
  
  <p>So I'd like to use <code>shared_ptr</code> to manage my objects, because it seems simple (also open to other ideas, but <strong>I don't want to add dependencies, like boost</strong>). </p>
  
  <p>But I'm having a little trouble with <code>shared_ptr</code>. I've tried updating the <code>std::set</code> to store <code>std::weak_ptr&lt;const T&gt;</code> with a comparison using <code>owner_before</code>.</p>
  
  <p>I need a <code>shared_ptr</code> to look up the object in the set. But that would require newing an object, and part of the point here is to be able to quickly get an existing structurally equal object.</p>
  
  <p><strong>Update</strong></p>
  
  <p>I also tried keeping the <code>set</code> as raw pointers, and using the <code>shared_ptr</code> deleter to remove elements. Alas, that requires me to use <code>shared_from_this</code> which seems to balk, though I'm not exactly sure why:</p>
  
  <pre><code>shape.cpp:30:16: error: member reference base type 'Circle *const' is not a
        structure or union
      return iter-&gt;shared_from_this();
             ~~~~^ ~~~~~~~~~~~~~~~~
  </code></pre>