<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>
        P1901R1 — Enabling the Use of weak_ptr as Keys in Unordered Associative
        Containers
    </title>
    <style>
        body {
            max-width: 40rem;
        }
        ins {
            color: green;
        }
        del {
            color: red;
        }
        blockquote {
            border-left: 0.5em solid #ddd;
            padding-left: 1em;
        }
    </style>
</head>
<body>
    <header>
        <table>
            <tr>
                <td>Document #:</td>
                <td>P1901R1</td>
            </tr>
            <tr>
                <td>Date:</td>
                <td>2020-02-15</td>
            </tr>
            <tr>
                <td>Reply-to:</td>
                <td>Daryl Haresign &lt;cpp@daryl.haresign.com&gt;</td>
            </tr>
            <tr>
                <td>Audience:</td>
                <td>Library Working Group</td>
            </tr>
        </table>
    </header>

    <hr>

    <main>
        <h1>
            Enabling the Use of <code>weak_ptr</code> as Keys in Unordered
            Associative Containers
        </h1>

        <h2 id="contents">Contents</h2>

        <ul>
            <li><a href="#abstract">Abstract</a></li>
            <li><a href="#history">History</a></li>
            <li><a href="#motivation">Motivation</a></li>
            <li><a href="#proposal">Proposal</a></li>
            <li><a href="#alternatives-considered">Alternatives Considered</a></li>
            <li><a href="#wording">Wording</a></li>
            <li><a href="#references">References</a></li>
            <li><a href="#acknowledgements">Acknowledgements</a></li>
        </ul>

        <h2 id="abstract">Abstract</h2>

        <p>
            This paper proposes the addition of ownership based hashing and
            equality, to enable the use of <code>weak_ptr</code> as keys in
            unordered associative containers.
        </p>

        <h2 id="history">History</h2>

        <ul>
            <li>
                R1:
                <ul>
                    <li>
                        Updated wording after LEWG review:
                        <ul>
                            <li>
                                Removed non-transparent class template
                                specializations
                            </li>
                            <li>
                                Added <code>is_transparent</code> to
                                <code>owner_hash</code>
                            </li>
                        </ul>
                    </li>
                </ul>
            </li>
            <li>
                R0:
                <ul>
                    <li>Initial version.</li>
                </ul>
            </li>
        </ul>

        <h2 id="motivation">Motivation</h2>

        <p>
            I recently found myself wanting to store a collection of
            <code>weak_ptr</code> objects.  I neither had a separate key nor
            any requirements on the ordering of the objects.  It seemed that
            using an <code>unordered_set</code> would be a good fit.  It
            quickly became apparent, however, that it’s simply not possible;
            there is no way to generate a stable hash or compare
            <code>weak_ptr</code> objects without support from the Standard
            Library.
        </p>

        <p>
            Also relevant is the discussion in LWG Issue 1406, which labelled
            this as not a defect, and recommended a paper be brought forward to
            propose the addition.
        </p>

        <h2 id="proposal">Proposal</h2>

        <h3><code>owner_hash</code> and <code>owner_equal</code></h3>

        <p>
            The obvious, and in my opinion best, option is to follow in the
            footsteps of <code>owner_less</code>, which can be used to allow
            <code>weak_ptr</code> keys in ordered associative containers.
        </p>

        <p>
            N2637 was the paper through which <code>owner_less</code> was
            introduced into the standard.
        </p>

        <p>
            The following methods and class templates would be added:
        </p>

        <ul>
            <li>
                <code>
                    size_t shared_ptr::owner_hash() const noexcept;
                </code>
            </li>
            <li>
                <code>
                    bool shared_ptr::owner_equal(…) const noexcept;
                </code>
            </li>
            <li>
                <code>
                    size_t weak_ptr::owner_hash() const noexcept;
                </code>
            </li>
            <li>
                <code>
                    bool weak_ptr::owner_equal(…) const noexcept;
                </code>
            </li>
            <li>
                <code>
                    template &lt;class T&gt; struct owner_hash;
                </code>
            </li>
            <li>
                <code>
                    template &lt;class T&gt; struct owner_equal;
                </code>
            </li>
        </ul>
        <p>
            <code>shared_ptr</code> and <code>weak_ptr</code> would gain two
            new methods each: <code>owner_hash</code> and
            <code>owner_equal</code>.  <code>owner_hash</code> would return a
            ownership based hash and <code>owner_equal</code> would compare
            ownership.
        </p>

        <p>
            In addition, two new class templates would be provided:
            <code>owner_hash</code> and <code>owner_equal</code>.
        </p>

        <p>
            This option has the benefit of providing all the tools necessary
            for having a <code>weak_ptr</code> key in an unordered associative
            container.
        </p>

        <p>
            The addition of the methods and template specializations for
            <code>shared_ptr</code> enables the ability to look up values in an
            unordered associative container from either the
            <code>weak_ptr</code> or the corresponding <code>shared_ptr</code>.
            It also enables storing <code>shared_ptr</code> keys based on the
            ownership equivalence relation, if such a thing is desired.
        </p>

        <p>
            I don't believe this proposal exposes any more implementation
            details than are already exposed through <code>owner_less</code>.
        </p>

        <p>
            To satisfy the <em>Cpp17Hash</em> requirements that the probability
            of a collision be low, a conforming implementation could simply
            hash the address of the control block, per
            <code>hash&lt;void *&gt;</code>.
        </p>

        <h2 id="alternatives-considered">Alternatives Considered</h2>

        <h3>
            <code>owner_ptr</code> / <code>owner</code> / <code>owner_id</code>
        </h3>

        <p>
            This option would instead add the following methods:
        </p>

        <ul>
            <li>
                <code>
                    const <em>unspecified</em> shared_ptr::owner_ptr() const
                    noexcept;
                </code>
            </li>
            <li>
                <code>
                    const <em>unspecified</em> weak_ptr::owner_ptr() const
                    noexcept;
                </code>
            </li>
        </ul>

        <p>
            The <em>unspecified</em> return type would need to be something for
            which there is, or could be, a <code>std::hash</code>
            specialization and an equality comparator (for example, a pointer
            to the control block, a guid stored in the control block, etc.).
        </p>

        <p>
            This option would look to provide the bare minimum required,
            leaving it up to the user to define <code>owner_hash</code> and
            <code>owner_equal</code> in terms of this method.
        </p>

        <h3><code>shared_ptr_key</code> / <code>weak_ptr_key</code></h3>

        <p>
            This option would provide wrapper classes,
            <code>shared_ptr_key</code> and <code>weak_ptr_key</code>, which
            would have a <code>std::hash</code> specialization and an equality
            comparator.  They would presumably be friends of
            <code>shared_ptr</code> and <code>weak_ptr</code> respectively.
        </p>

        <h3>Do Nothing</h3>

        <p>
            Whilst writing this paper, I discovered that it is actually
            possible for end users to do this right now in terms of
            <code>owner_before</code>, it just wouldn't be efficient.
        </p>

        <p>
            <code>owner_hash</code> would be implemented to return a constant
            value &mdash; completely going against the <em>Cpp17Hash</em>
            requirements which state thatthe probability of a collision be
            low.
        </p>

        <p>
            and
        </p>

        <p>
            <code>owner_equal(p,q)</code> would be implemented to return
            <code>!p.owner_before(q) &amp;&amp; !q.owner_before(p)</code>.
        </p>

        <h2 id="wording">Wording</h2>

        <h3>Class <code>owner_hash</code> [util.smartptr.ownerhash]</h3>

        <p>
            Add this new section under [util.smartptr].
        </p>

        <blockquote><ins>
            <h3>Class <code>owner_hash</code> [util.smartptr.ownerhash]</h3>

            <p>
                The class <code>owner_hash</code> allows ownership-based
                hashing.
            </p>

            <pre>namespace std {
  struct owner_hash {
    template &lt;class T&gt;
      size_t operator()(const shared_ptr&lt;T&gt;&amp;) const noexcept;

    template &lt;class T&gt;
      size_t operator()(const weak_ptr&lt;T&gt;&amp;) const noexcept;

    using is_transparent = <em>unspecified</em>;
  };
}</pre>

            <pre>template &lt;class T&gt;
  size_t operator()(const shared_ptr&lt;T&gt;&amp; x) const noexcept;
template &lt;class T&gt;
  size_t operator()(const weak_ptr&lt;T&gt;&amp;) const noexcept;</pre>

            <ul>
                <li><em>Effects: returns <code>x.owner_hash()</code></em>.</li>
            </ul>
        </ins></blockquote>

        <h3>Class <code>owner_equal</code> [util.smartptr.ownerequal]</h3>

        <p>
            Add this new section under [util.smartptr].
        </p>

        <blockquote><ins>
            <h3>Class <code>owner_equal</code> [util.smartptr.ownerequal]</h3>

            <p>
                The class <code>owner_equal</code> allows ownership-based mixed
                equality comparisons of shared and weak pointers.
            </p>

            <pre>namespace std {
  struct owner_equal {
    template &lt;class T, class U&gt;
      bool operator()(const shared_ptr&lt;T&gt;&amp;, const shared_ptr&lt;U&gt;&amp;) const noexcept;
    template &lt;class T, class U&gt;
      bool operator()(const shared_ptr&lt;T&gt;&amp;, const weak_ptr&lt;U&gt;&amp;) const noexcept;
    template &lt;class T, class U&gt;
      bool operator()(const weak_ptr&lt;T&gt;&amp;, const shared_ptr&lt;U&gt;&amp;) const noexcept;
    template &lt;class T, class U&gt;
      bool operator()(const weak_ptr&lt;T&gt;&amp;, const weak_ptr&lt;U&gt;&amp;) const noexcept;

    using is_transparent = <em>unspecified</em>;
  };
}</pre>

            <pre>template &lt;class T, class U&gt;
  bool operator()(const shared_ptr&lt;T&gt;&amp; x, const shared_ptr&lt;U&gt;&amp; y) const noexcept;
template &lt;class T, class U&gt;
  bool operator()(const shared_ptr&lt;T&gt;&amp; x, const weak_ptr&lt;U&gt;&amp; y) const noexcept;
template &lt;class T, class U&gt;
  bool operator()(const weak_ptr&lt;T&gt;&amp; x, const shared_ptr&lt;U&gt;&amp; y) const noexcept;
template &lt;class T, class U&gt;
  bool operator()(const weak_ptr&lt;T&gt;&amp; x, const weak_ptr&lt;U&gt;&amp; y) const noexcept;</pre>

            <ul>
                <li><em>Effects:</em> Returns <code>x.owner_equal(y)</code>.</li>
            </ul>
        </ins></blockquote>

        <h3>Class template <code>shared_ptr</code> [util.smartptr.shared]</h3>

        <p>
            Add to the observers section:
        </p>

        <blockquote><ins>
            <pre>size_t owner_hash() const noexcept;
template&lt;class U&gt;
  bool owner_equal(const shared_ptr&lt;U&gt;&amp; b) const noexcept;
template&lt;class U&gt;
  bool owner_equal(const weak_ptr&lt;U&gt;&amp; b) const noexcept;</pre>
        </ins></blockquote>

        <h3>Observers [util.smartptr.shared.obs]</h3>

        <p>
            Add at the end:
        </p>

        <ins><blockquote>
            <pre>size_t owner_hash() const noexcept;</pre>

            <ul>
                <li>
                    <em>Effects:</em> Returns an unspecified value such that
                    <p>
                        &mdash; <code>x.owner_hash() == y.owner_hash()</code>
                        is <code>true</code> if
                        <code>x.owner_equal() == y.owner_equal()</code> is
                        <code>true</code>.
                    </p>
                </li>
            </ul>

            <pre>template&lt;class U&gt;
  bool owner_equal(const shared_ptr&lt;U&gt;&amp; b) const noexcept;
template&lt;class U&gt;
  bool owner_equal(const weak_ptr&lt;U&gt;&amp; b) const noexcept;</pre>

            <ul>
                <li>
                    <em>Effects:</em> Returns <code>true</code> if
                    <code>*this</code> and <code>b</code> share ownership or
                    are both empty.  Otherwise returns <code>false</code>.
                </li>
            </ul>
        </blockquote></ins>

        <h3>Class template <code>weak_ptr</code> [util.smartptr.weak]</h3>

        <p>
            Add to the observers section:
        </p>

        <ins><blockquote>
            <pre>size_t owner_hash() const noexcept;
template&lt;class U&gt;
  bool owner_equal(const shared_ptr&lt;U&gt;&amp; b) const noexcept;
template&lt;class U&gt;
  bool owner_equal(const weak_ptr&lt;U&gt;&amp; b) const noexcept;</pre>
        </blockquote></ins>

        <h3>Observers [util.smartptr.weak.obs]</h3>

        <p>
            Add at the end:
        </p>

        <ins><blockquote>
            <pre>size_t owner_hash() const noexcept;</pre>

            <ul>
                <li>
                    <em>Effects:</em> Returns an unspecified value such that
                    <p>
                        &mdash; <code>x.owner_hash() == y.owner_hash()</code>
                        is <code>true</code> if
                        <code>x.owner_equal() == y.owner_equal()</code> is
                        <code>true</code>.
                    </p>
                </li>
            </ul>

            <pre>template&lt;class U&gt;
  bool owner_equal(const shared_ptr&lt;U&gt;&amp; b) const noexcept;
template&lt;class U&gt;
  bool owner_equal(const weak_ptr&lt;U&gt;&amp; b) const noexcept;</pre>

            <ul>
                <li>
                    <em>Effects:</em> Returns <code>true</code> if
                    <code>*this</code> and <code>b</code> share ownership or
                    are both empty.  Otherwise returns <code>false</code>.
                </li>
            </ul>
        </blockquote></ins>

        <h3>Class template <code>enable_shared_from_this</code> [util.smartptr.enab]</h3>

        <p>
            It might make sense to change the example <code>assert</code> as
            follows:
        </p>

        <pre>assert(<del>!p.owner_before(q) &amp;&amp; !q.owner_before(p)</del><ins>p.owner_equal(q)</ins>); // p and q share ownership</pre>

        <h2 id="references">References</h2>

        <ul>
            <li>
                LWG Issue 1406 &mdash;
                <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-closed.html#1406">http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-closed.html#1406</a>
            </li>
            <li>
                N2637 Revisiting <code>std::shared_ptr</code> comparison &mdash;
                <a href="https://wg21.link/n2637">https://wg21.link/n2637</a>
            </li>
            <li>
                <em>Cpp17Hash</em> requirements &mdash; [tab:cpp17.hash] in
                <a href="https://wg21.link/n4830">https://wg21.link/n4830</a>
            </li>
        </ul>

        <h2 id="acknowledgements">Acknowledgements</h2>

        <p>
            Thanks to Alexander Jones, Alisdair Meredith and Masud Rahman for
            their help reviewing, suggesting alternatives, and providing
            background on the history of <code>owner_less</code>.
        </p>
    </main>
</body>
</html>
