﻿<html>
<head>
<title>Heterogenous extensions to unordered containers</title>
</head>
<body>
<p>Document Number: N3573</p>
<p>Date: 2013-03-10</p>
<p>Project: Programming Language C++, Library Working Group</p>
<p>Reply-to: Mark Boyall wolfeinstein@gmail.com</p>
<h1>Heterogenous extensions to unordered containers</h1>
<p>The rationale for this proposal is a simple pair of use cases, and extends the Standard unordered_map and unordered_set to permit them. The first is the use of alternative types as key. For example, currently, it is 
    impossible to look up by a type other than the key type. This sounds reasonable but is actually fairly limiting. Consider
</p>
<div class="well"><code>
int main() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;std::unordered_set&lt;std::unique_ptr&lt;T&gt;&gt; set;<br />
}</code></div>
<p>Whilst it's possible to insert into the set, there is no means to erase or test for membership, as that would involve constructing two unique_ptr to the same resource. However, there is no implementation reason to 
    prevent lookup by an arbitrary key type, T, as long as hash(t) == hash(k) for any key k in the map, if t == k.
</p>
<p>Secondly, it is impossible to override the hash or equivalence function for a given lookup. This can be used for localized optimizations- especially caching. For example,</p>
<div class="well"><code>
int main() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;std::unordered_map&lt;std::string, int&gt; map;<br />
&nbsp;&nbsp;&nbsp;&nbsp;std::string value;<br />
&nbsp;&nbsp;&nbsp;&nbsp;std::size_t hash_cache;<br />
&nbsp;&nbsp;&nbsp;&nbsp;bool dirty;<br />
&nbsp;&nbsp;&nbsp;&nbsp; // later<br />
&nbsp;&nbsp;&nbsp;&nbsp;map.find(value, [&](std::string& val) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (!dirty) return hash_cache; else return std::hash&lt;&gt;()(val);<br />
&nbsp;&nbsp;&nbsp;&nbsp;});<br />
}<br />
</code></div>
<p>Additionally, in <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3421.htm">n3421</a>, std::hash was left out of the list of function objects which are now more generic. This proposal would add it. 
    The templated function call operator of std::hash<>() would forward it to the explicit argument version.</p>
<div class="well"><code>
namespace std {<br />
&nbsp;&nbsp;&nbsp;&nbsp;template&lt;typename T = void&gt; struct hash;<br />
&nbsp;&nbsp;&nbsp;&nbsp;template&lt;&gt; struct hash&lt;void&gt; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;template&lt;typename T&gt std::size_t operator()(T&& t) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return std::hash&lt;typename std::decay&lt;T&gt;::type&gt()(std::forward&lt;T&gt;(t));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;};<br />
}
</code></div>
<p>These two interface limitations are overcome by presenting new functions. Whilst insertion cannot be further generalized, erasure and lookup both can be.</p>
<div class="well"><code>
template&lt;typename T, typename Hash = std::hash&lt;&gt;, typename Eq = std::equal_to&lt;&gt;&gt;<br />
iterator find(T t, Hash h = Hash(), Eq e = Eq());<br />
template&lt;typename T, typename Hash = std::hash&lt;&gt;, typename Eq = std::equal_to&lt;&gt;&gt;<br />
const_iterator find(T t, Hash h = Hash(), Eq e = Eq()) const;
</code></div>
<p>In this case, h(t) gives the hash value of t, and e(t, k) returns whether or not t is equal to k, for some key k. Also proposed are similar variants for erase(), and operator[] for unordered_map. The operator 
    can't take the additional function object arguments, so std::hash&lt;&gt; and std::equal_to&lt;&gt; will be used. Given these functions, it's quite possible to implement both of the use cases described above.
</p>
</body></html>