diff mbox series

[Hashtable,5/6] Remove H1/H2 template parameters

Message ID 93d1a4b7-bbea-2766-9a71-6ba1a789d0da@gmail.com
State New
Headers show
Series None | expand

Commit Message

François Dumont Nov. 17, 2019, 9:15 p.m. UTC
H1 used to be a reference to the user Hash, now _Hashtable and unordered 
types agree on the same Hash type which is more intuitive.

I also chose to not support anymore a stateful ranged hash functor. We 
only use _Mod_range_hashing and _Mask_range_hashing.

Thanks to this simplification _M_bucket_index can also be simplified.

     * include/bits/hashtable_policy.h (_Hashtable<>): Remove _H1 and _H2
     template parameters.
     (_Hastable_base<>): Likewise.
     (_Default_ranged_hash): Remove.
     (_Prime_rehash_policy::__ranged_hash): New.
     (_Power2_rehash_policy::__ranged_hash): New.
     (_Map_base<>): Remove _H1 and _H2 template parameters.
     (_Insert_base<>): Likewise.
     (_Insert<>): Likewise.
     (_Rehash_base<>): Likewise.
     (_Local_iterator_base<>): Remove _H1 and _H2 template parameters 
and add
     _RangedHash.
     (_Hash_code_base<>): Likewise.
     (_Hash_code_base<_Key, _Value, _ExtractKey, _H1, _H2, _Hash,
     __hash_not_cached_t>): Remove.
     (_Hash_code_base<>::_M_bucket_index(const _Key&, __hash_code, size_t)):
     Replace by...
     (_Hash_code_base<>::_M_bucket_index(__hash_code, size_t)): ...this.
     (_Local_iterator<>): Remove _H1 and _H2 template parameters.
     (_Local_const_iterator<>): Likewise.
     (_Equality<>): Likewise.
     * include/bits/hashtable.h (_Hashtable<>): Remove _H1 and _H2 template
     parameters.
     * include/bits/node_handle.h: Adapt.
     * include/bits/unordered_map.h: Adapt.
     * include/bits/unordered_set.h: Adapt.
     * testsuite/23_containers/unordered_set/hash_policy/26132.cc: Adapt.
     * testsuite/23_containers/unordered_set/hash_policy/71181.cc: Adapt.
     * testsuite/23_containers/unordered_set/hash_policy/load_factor.cc:
     Adapt.
     * testsuite/23_containers/unordered_set/hash_policy/rehash.cc: Adapt.
     * testsuite/23_containers/unordered_set/insert/hash_policy.cc: Adapt.
     * testsuite/23_containers/unordered_set/max_load_factor/robustness.cc:
     Adapt.
     * testsuite/performance/23_containers/insert/54075.cc: Adapt.
     * testsuite/performance/23_containers/insert_erase/41975.cc: Adapt.
     * testsuite/performance/23_containers/insert_erase/
     unordered_small_size.cc: Adapt.

Tested under Linux x86_64.

François

Comments

Ville Voutilainen Nov. 17, 2019, 10:21 p.m. UTC | #1
On Sun, 17 Nov 2019 at 23:15, François Dumont <frs.dumont@gmail.com> wrote:
>
> H1 used to be a reference to the user Hash, now _Hashtable and unordered
> types agree on the same Hash type which is more intuitive.
>
> I also chose to not support anymore a stateful ranged hash functor. We
> only use _Mod_range_hashing and _Mask_range_hashing.
>
> Thanks to this simplification _M_bucket_index can also be simplified.

Do we know whether there are existing users that this breaks? Also, is
this ABI-compatible
for our unordered containers?
François Dumont Nov. 18, 2019, 9:40 p.m. UTC | #2
On 11/17/19 11:21 PM, Ville Voutilainen wrote:
> On Sun, 17 Nov 2019 at 23:15, François Dumont <frs.dumont@gmail.com> wrote:
>> H1 used to be a reference to the user Hash, now _Hashtable and unordered
>> types agree on the same Hash type which is more intuitive.
>>
>> I also chose to not support anymore a stateful ranged hash functor. We
>> only use _Mod_range_hashing and _Mask_range_hashing.
>>
>> Thanks to this simplification _M_bucket_index can also be simplified.
> Do we know whether there are existing users that this breaks?

That's the problem with this kind of extension, we don't know.

However we never get any report neither about potential users. And I 
remember that I fixed some years ago a bug that users would have 
reported much sooner if there have been users. But it was some years ago.

>   Also, is
> this ABI-compatible
> for our unordered containers?
>
IMHO, yes it is.

In hashtable_policy.h _H1 was the user hash which I renamed in _Hash, 
the same template name that for unordered containers.

_H2 was always _Mod_range_hashing and previous _Hash always 
_Default_ranged_hash, both stateless types. Only _H1 and _H2 were stored 
in _Hash_code_base for instance. _H1 is still as _Hash and _H2 was just 
empty and moreover stored last so removing it has no impact.

François
Ville Voutilainen Nov. 18, 2019, 10:10 p.m. UTC | #3
On Mon, 18 Nov 2019 at 23:41, François Dumont <frs.dumont@gmail.com> wrote:
> >   Also, is
> > this ABI-compatible
> > for our unordered containers?
> >
> IMHO, yes it is.
>
> In hashtable_policy.h _H1 was the user hash which I renamed in _Hash,
> the same template name that for unordered containers.
>
> _H2 was always _Mod_range_hashing and previous _Hash always
> _Default_ranged_hash, both stateless types. Only _H1 and _H2 were stored
> in _Hash_code_base for instance. _H1 is still as _Hash and _H2 was just
> empty and moreover stored last so removing it has no impact.

Right, and as I understood the code, it's all empty base objects
anyway, so it boils down to whether
a set of empty bases can change without causing an ABI break, and I
don't think there's an ABI
problem here because the actual _Hashtable is stored as a member of
various containers, and its
size doesn't change, and based on what you wrote, with which my
code-reading agrees, the behaviour
doesn't change either. So the patch seems okay to me.
François Dumont Dec. 19, 2019, 7:22 p.m. UTC | #4
Because of this change printers.py has to be updated too.

François


On 11/17/19 10:15 PM, François Dumont wrote:
> H1 used to be a reference to the user Hash, now _Hashtable and 
> unordered types agree on the same Hash type which is more intuitive.
>
> I also chose to not support anymore a stateful ranged hash functor. We 
> only use _Mod_range_hashing and _Mask_range_hashing.
>
> Thanks to this simplification _M_bucket_index can also be simplified.
>
>     * include/bits/hashtable_policy.h (_Hashtable<>): Remove _H1 and _H2
>     template parameters.
>     (_Hastable_base<>): Likewise.
>     (_Default_ranged_hash): Remove.
>     (_Prime_rehash_policy::__ranged_hash): New.
>     (_Power2_rehash_policy::__ranged_hash): New.
>     (_Map_base<>): Remove _H1 and _H2 template parameters.
>     (_Insert_base<>): Likewise.
>     (_Insert<>): Likewise.
>     (_Rehash_base<>): Likewise.
>     (_Local_iterator_base<>): Remove _H1 and _H2 template parameters 
> and add
>     _RangedHash.
>     (_Hash_code_base<>): Likewise.
>     (_Hash_code_base<_Key, _Value, _ExtractKey, _H1, _H2, _Hash,
>     __hash_not_cached_t>): Remove.
>     (_Hash_code_base<>::_M_bucket_index(const _Key&, __hash_code, 
> size_t)):
>     Replace by...
>     (_Hash_code_base<>::_M_bucket_index(__hash_code, size_t)): ...this.
>     (_Local_iterator<>): Remove _H1 and _H2 template parameters.
>     (_Local_const_iterator<>): Likewise.
>     (_Equality<>): Likewise.
>     * include/bits/hashtable.h (_Hashtable<>): Remove _H1 and _H2 
> template
>     parameters.
>     * include/bits/node_handle.h: Adapt.
>     * include/bits/unordered_map.h: Adapt.
>     * include/bits/unordered_set.h: Adapt.
>     * testsuite/23_containers/unordered_set/hash_policy/26132.cc: Adapt.
>     * testsuite/23_containers/unordered_set/hash_policy/71181.cc: Adapt.
>     * testsuite/23_containers/unordered_set/hash_policy/load_factor.cc:
>     Adapt.
>     * testsuite/23_containers/unordered_set/hash_policy/rehash.cc: Adapt.
>     * testsuite/23_containers/unordered_set/insert/hash_policy.cc: Adapt.
>     * 
> testsuite/23_containers/unordered_set/max_load_factor/robustness.cc:
>     Adapt.
>     * testsuite/performance/23_containers/insert/54075.cc: Adapt.
>     * testsuite/performance/23_containers/insert_erase/41975.cc: Adapt.
>     * testsuite/performance/23_containers/insert_erase/
>     unordered_small_size.cc: Adapt.
>
> Tested under Linux x86_64.
>
> François
>
>
Jonathan Wakely July 17, 2020, 10:51 a.m. UTC | #5
On 19/11/19 00:10 +0200, Ville Voutilainen wrote:
>On Mon, 18 Nov 2019 at 23:41, François Dumont <frs.dumont@gmail.com> wrote:
>> >   Also, is
>> > this ABI-compatible
>> > for our unordered containers?
>> >
>> IMHO, yes it is.
>>
>> In hashtable_policy.h _H1 was the user hash which I renamed in _Hash,
>> the same template name that for unordered containers.
>>
>> _H2 was always _Mod_range_hashing and previous _Hash always
>> _Default_ranged_hash, both stateless types. Only _H1 and _H2 were stored
>> in _Hash_code_base for instance. _H1 is still as _Hash and _H2 was just
>> empty and moreover stored last so removing it has no impact.
>
>Right, and as I understood the code, it's all empty base objects
>anyway, so it boils down to whether
>a set of empty bases can change without causing an ABI break, and I
>don't think there's an ABI
>problem here because the actual _Hashtable is stored as a member of
>various containers, and its
>size doesn't change, and based on what you wrote, with which my
>code-reading agrees, the behaviour
>doesn't change either. So the patch seems okay to me.

There's no guarantee that a program-defined hash object is actually
empty.
Jonathan Wakely July 17, 2020, 11:35 a.m. UTC | #6
On 19/12/19 20:22 +0100, François Dumont wrote:
>Because of this change printers.py has to be updated too.
>
>François
>
>
>On 11/17/19 10:15 PM, François Dumont wrote:
>>H1 used to be a reference to the user Hash, now _Hashtable and 
>>unordered types agree on the same Hash type which is more intuitive.
>>
>>I also chose to not support anymore a stateful ranged hash functor. 
>>We only use _Mod_range_hashing and _Mask_range_hashing.
>>
>>Thanks to this simplification _M_bucket_index can also be simplified.
>>
>>    * include/bits/hashtable_policy.h (_Hashtable<>): Remove _H1 and _H2
>>    template parameters.
>>    (_Hastable_base<>): Likewise.
>>    (_Default_ranged_hash): Remove.
>>    (_Prime_rehash_policy::__ranged_hash): New.
>>    (_Power2_rehash_policy::__ranged_hash): New.
>>    (_Map_base<>): Remove _H1 and _H2 template parameters.
>>    (_Insert_base<>): Likewise.
>>    (_Insert<>): Likewise.
>>    (_Rehash_base<>): Likewise.
>>    (_Local_iterator_base<>): Remove _H1 and _H2 template parameters 
>>and add
>>    _RangedHash.
>>    (_Hash_code_base<>): Likewise.
>>    (_Hash_code_base<_Key, _Value, _ExtractKey, _H1, _H2, _Hash,
>>    __hash_not_cached_t>): Remove.
>>    (_Hash_code_base<>::_M_bucket_index(const _Key&, __hash_code, 
>>size_t)):
>>    Replace by...
>>    (_Hash_code_base<>::_M_bucket_index(__hash_code, size_t)): ...this.
>>    (_Local_iterator<>): Remove _H1 and _H2 template parameters.
>>    (_Local_const_iterator<>): Likewise.
>>    (_Equality<>): Likewise.
>>    * include/bits/hashtable.h (_Hashtable<>): Remove _H1 and _H2 
>>template
>>    parameters.
>>    * include/bits/node_handle.h: Adapt.
>>    * include/bits/unordered_map.h: Adapt.
>>    * include/bits/unordered_set.h: Adapt.
>>    * testsuite/23_containers/unordered_set/hash_policy/26132.cc: Adapt.
>>    * testsuite/23_containers/unordered_set/hash_policy/71181.cc: Adapt.
>>    * testsuite/23_containers/unordered_set/hash_policy/load_factor.cc:
>>    Adapt.
>>    * testsuite/23_containers/unordered_set/hash_policy/rehash.cc: Adapt.
>>    * testsuite/23_containers/unordered_set/insert/hash_policy.cc: Adapt.
>>    * 
>>testsuite/23_containers/unordered_set/max_load_factor/robustness.cc:
>>    Adapt.
>>    * testsuite/performance/23_containers/insert/54075.cc: Adapt.
>>    * testsuite/performance/23_containers/insert_erase/41975.cc: Adapt.
>>    * testsuite/performance/23_containers/insert_erase/
>>    unordered_small_size.cc: Adapt.
>>
>>Tested under Linux x86_64.
>>
>>François
>>
>>
>

>diff --git a/libstdc++-v3/include/bits/hashtable.h b/libstdc++-v3/include/bits/hashtable.h
>index 41be821782d..9dadc62e328 100644
>--- a/libstdc++-v3/include/bits/hashtable.h
>+++ b/libstdc++-v3/include/bits/hashtable.h
>@@ -69,31 +69,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>    *  and returns a bool-like value that is true if the two objects
>    *  are considered equal.
>    *
>-   *  @tparam _H1  The hash function. A unary function object with
>+   *  @tparam _Hash  The hash function. A unary function object with

Renaming this to _Hash is great, that makes the code much easier to
understand for a casual reader (like me every time I have to remember
how our hash tables work).

It makes much more sense for the _Hash parameter of unordered_set and
the _Hash parameter of _Hashtable to be the same thing!

>    *  argument type _Key and result type size_t. Return values should
>    *  be distributed over the entire range [0, numeric_limits<size_t>:::max()].
>    *
>-   *  @tparam _H2  The range-hashing function (in the terminology of
>-   *  Tavori and Dreizin).  A binary function object whose argument
>-   *  types and result type are all size_t.  Given arguments r and N,
>-   *  the return value is in the range [0, N).
>-   *
>-   *  @tparam _Hash  The ranged hash function (Tavori and Dreizin). A
>-   *  binary function whose argument types are _Key and size_t and
>-   *  whose result type is size_t.  Given arguments k and N, the
>-   *  return value is in the range [0, N).  Default: hash(k, N) =
>-   *  h2(h1(k), N).  If _Hash is anything other than the default, _H1
>-   *  and _H2 are ignored.

But I would prefer to keep these parameters, and just rename them to
_Unused1 and _Unused2 or something like that. Maybe _U1 and _U2.

You can still get rid of them as constructor parameters and get rid of
the base classes using those types. Just keep them in the template
argument lists, so that we keep generating the same mangled names for
specializations of _Hashtable.

That way you don't need to change printers.py (which is good, because
that change to printers.py is not OK anyway - it would make it
impossible to debug code built with older versions of GCC, because the
printers would only support the new layout - we need to support
debugging programs that were not all built by the same version of
GCC).

>@@ -168,19 +155,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>    */
>   template<typename _Key, typename _Value, typename _Alloc,
> 	   typename _ExtractKey, typename _Equal,
>-	   typename _H1, typename _H2, typename _Hash,
>-	   typename _RehashPolicy, typename _Traits>
>+	   typename _Hash, typename _RehashPolicy, typename _Traits>

So to be clear, what I'm suggesting is:

   template<typename _Key, typename _Value, typename _Alloc,
	   typename _ExtractKey, typename _Equal,
	   typename _Hash, typename _u1, typename _U2,
	   typename _RehashPolicy, typename _Traits>
     class _Hashtable
     : public __detail::_Hashtable_base<_Key, _Value, _ExtractKey, _Equal,
				       _Hash, _U1, _U2, _Traits>,

So the parameters are still there at each level of the hierarchy.

>@@ -456,15 +434,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       void
>       _M_reset() noexcept;
> 
>-      _Hashtable(const _H1& __h1, const _H2& __h2, const _Hash& __h,
>-		 const _Equal& __eq, const _ExtractKey& __exk,
>+      _Hashtable(const _Hash& __h, const _Equal& __eq, const _ExtractKey& __exk,
> 		 const allocator_type& __a)
>-      : __hashtable_base(__exk, __h1, __h2, __h, __eq),
>+      : __hashtable_base(__exk, __h, __eq),
> 	__hashtable_alloc(__node_alloc_type(__a))
>       { }

This change is still fine. There's no point passing around objects of
the types that will now be unused.

>diff --git a/libstdc++-v3/include/bits/hashtable_policy.h b/libstdc++-v3/include/bits/hashtable_policy.h
>index 5cc943b3d22..11ea47b322e 100644
>--- a/libstdc++-v3/include/bits/hashtable_policy.h
>+++ b/libstdc++-v3/include/bits/hashtable_policy.h
>@@ -41,8 +41,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 
>   template<typename _Key, typename _Value, typename _Alloc,
> 	   typename _ExtractKey, typename _Equal,
>-	   typename _H1, typename _H2, typename _Hash,
>-	   typename _RehashPolicy, typename _Traits>
>+	   typename _Hash, typename _RehashPolicy,
>+	   typename _Traits>
>     class _Hashtable;
> 
> namespace __detail
>@@ -54,7 +54,8 @@ namespace __detail
>    */
>   template<typename _Key, typename _Value,
> 	   typename _ExtractKey, typename _Equal,
>-	   typename _H1, typename _H2, typename _Hash, typename _Traits>
>+	   typename _Hash, typename _RangedHash,
>+	   typename _Traits>
>     struct _Hashtable_base;
> 
>   // Helper function: return distance(first, last) for forward
>@@ -442,18 +443,12 @@ namespace __detail
>     { return __num % __den; }
>   };
> 
>-  /// Default ranged hash function H.  In principle it should be a
>-  /// function object composed from objects of type H1 and H2 such that
>-  /// h(k, N) = h2(h1(k), N), but that would mean making extra copies of
>-  /// h1 and h2.  So instead we'll just use a tag to tell class template
>-  /// hashtable to do that composition.
>-  struct _Default_ranged_hash { };

We'd still need to keep this empty type, so that it's still used by
the aliases like __umap_hashtable and still appears in mangled names.

Does my suggestion make sense?

I really like the general idea of getting rid of some of the
complexity and not supporting infinite customization. But we can do
that without changing mangled names of the _Hashtable specialiations.
François Dumont Aug. 6, 2020, 6:35 a.m. UTC | #7
On 17/07/20 1:35 pm, Jonathan Wakely wrote:
> On 19/12/19 20:22 +0100, François Dumont wrote:
>> Because of this change printers.py has to be updated too.
>>
>> François
>>
>>
>> On 11/17/19 10:15 PM, François Dumont wrote:
>>> H1 used to be a reference to the user Hash, now _Hashtable and 
>>> unordered types agree on the same Hash type which is more intuitive.
>>>
>>> I also chose to not support anymore a stateful ranged hash functor. 
>>> We only use _Mod_range_hashing and _Mask_range_hashing.
>>>
>>> Thanks to this simplification _M_bucket_index can also be simplified.
>>>
>>>     * include/bits/hashtable_policy.h (_Hashtable<>): Remove _H1 
>>> and _H2
>>>     template parameters.
>>>     (_Hastable_base<>): Likewise.
>>>     (_Default_ranged_hash): Remove.
>>>     (_Prime_rehash_policy::__ranged_hash): New.
>>>     (_Power2_rehash_policy::__ranged_hash): New.
>>>     (_Map_base<>): Remove _H1 and _H2 template parameters.
>>>     (_Insert_base<>): Likewise.
>>>     (_Insert<>): Likewise.
>>>     (_Rehash_base<>): Likewise.
>>>     (_Local_iterator_base<>): Remove _H1 and _H2 template 
>>> parameters and add
>>>     _RangedHash.
>>>     (_Hash_code_base<>): Likewise.
>>>     (_Hash_code_base<_Key, _Value, _ExtractKey, _H1, _H2, _Hash,
>>>     __hash_not_cached_t>): Remove.
>>>     (_Hash_code_base<>::_M_bucket_index(const _Key&, __hash_code, 
>>> size_t)):
>>>     Replace by...
>>>     (_Hash_code_base<>::_M_bucket_index(__hash_code, size_t)): 
>>> ...this.
>>>     (_Local_iterator<>): Remove _H1 and _H2 template parameters.
>>>     (_Local_const_iterator<>): Likewise.
>>>     (_Equality<>): Likewise.
>>>     * include/bits/hashtable.h (_Hashtable<>): Remove _H1 and _H2 
>>> template
>>>     parameters.
>>>     * include/bits/node_handle.h: Adapt.
>>>     * include/bits/unordered_map.h: Adapt.
>>>     * include/bits/unordered_set.h: Adapt.
>>>     * testsuite/23_containers/unordered_set/hash_policy/26132.cc: 
>>> Adapt.
>>>     * testsuite/23_containers/unordered_set/hash_policy/71181.cc: 
>>> Adapt.
>>>     * 
>>> testsuite/23_containers/unordered_set/hash_policy/load_factor.cc:
>>>     Adapt.
>>>     * 
>>> testsuite/23_containers/unordered_set/hash_policy/rehash.cc: Adapt.
>>>     * 
>>> testsuite/23_containers/unordered_set/insert/hash_policy.cc: Adapt.
>>>     * 
>>> testsuite/23_containers/unordered_set/max_load_factor/robustness.cc:
>>>     Adapt.
>>>     * testsuite/performance/23_containers/insert/54075.cc: Adapt.
>>>     * testsuite/performance/23_containers/insert_erase/41975.cc: 
>>> Adapt.
>>>     * testsuite/performance/23_containers/insert_erase/
>>>     unordered_small_size.cc: Adapt.
>>>
>>> Tested under Linux x86_64.
>>>
>>> François
>>>
>>>
>>
>
>> diff --git a/libstdc++-v3/include/bits/hashtable.h 
>> b/libstdc++-v3/include/bits/hashtable.h
>> index 41be821782d..9dadc62e328 100644
>> --- a/libstdc++-v3/include/bits/hashtable.h
>> +++ b/libstdc++-v3/include/bits/hashtable.h
>> @@ -69,31 +69,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>    *  and returns a bool-like value that is true if the two objects
>>    *  are considered equal.
>>    *
>> -   *  @tparam _H1  The hash function. A unary function object with
>> +   *  @tparam _Hash  The hash function. A unary function object with
>
> Renaming this to _Hash is great, that makes the code much easier to
> understand for a casual reader (like me every time I have to remember
> how our hash tables work).
>
> It makes much more sense for the _Hash parameter of unordered_set and
> the _Hash parameter of _Hashtable to be the same thing!
>
>>    *  argument type _Key and result type size_t. Return values should
>>    *  be distributed over the entire range [0, 
>> numeric_limits<size_t>:::max()].
>>    *
>> -   *  @tparam _H2  The range-hashing function (in the terminology of
>> -   *  Tavori and Dreizin).  A binary function object whose argument
>> -   *  types and result type are all size_t.  Given arguments r and N,
>> -   *  the return value is in the range [0, N).
>> -   *
>> -   *  @tparam _Hash  The ranged hash function (Tavori and Dreizin). A
>> -   *  binary function whose argument types are _Key and size_t and
>> -   *  whose result type is size_t.  Given arguments k and N, the
>> -   *  return value is in the range [0, N).  Default: hash(k, N) =
>> -   *  h2(h1(k), N).  If _Hash is anything other than the default, _H1
>> -   *  and _H2 are ignored.
>
> But I would prefer to keep these parameters, and just rename them to
> _Unused1 and _Unused2 or something like that. Maybe _U1 and _U2.
>
> You can still get rid of them as constructor parameters and get rid of
> the base classes using those types. Just keep them in the template
> argument lists, so that we keep generating the same mangled names for
> specializations of _Hashtable.
>
> That way you don't need to change printers.py (which is good, because
> that change to printers.py is not OK anyway - it would make it
> impossible to debug code built with older versions of GCC, because the
> printers would only support the new layout - we need to support
> debugging programs that were not all built by the same version of
> GCC).
>
>> @@ -168,19 +155,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>    */
>>   template<typename _Key, typename _Value, typename _Alloc,
>>        typename _ExtractKey, typename _Equal,
>> -       typename _H1, typename _H2, typename _Hash,
>> -       typename _RehashPolicy, typename _Traits>
>> +       typename _Hash, typename _RehashPolicy, typename _Traits>
>
> So to be clear, what I'm suggesting is:
>
>   template<typename _Key, typename _Value, typename _Alloc,
>        typename _ExtractKey, typename _Equal,
>        typename _Hash, typename _u1, typename _U2,
>        typename _RehashPolicy, typename _Traits>
>     class _Hashtable
>     : public __detail::_Hashtable_base<_Key, _Value, _ExtractKey, _Equal,
>                        _Hash, _U1, _U2, _Traits>,
>
> So the parameters are still there at each level of the hierarchy.
>
>> @@ -456,15 +434,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>       void
>>       _M_reset() noexcept;
>>
>> -      _Hashtable(const _H1& __h1, const _H2& __h2, const _Hash& __h,
>> -         const _Equal& __eq, const _ExtractKey& __exk,
>> +      _Hashtable(const _Hash& __h, const _Equal& __eq, const 
>> _ExtractKey& __exk,
>>          const allocator_type& __a)
>> -      : __hashtable_base(__exk, __h1, __h2, __h, __eq),
>> +      : __hashtable_base(__exk, __h, __eq),
>>     __hashtable_alloc(__node_alloc_type(__a))
>>       { }
>
> This change is still fine. There's no point passing around objects of
> the types that will now be unused.
>
>> diff --git a/libstdc++-v3/include/bits/hashtable_policy.h 
>> b/libstdc++-v3/include/bits/hashtable_policy.h
>> index 5cc943b3d22..11ea47b322e 100644
>> --- a/libstdc++-v3/include/bits/hashtable_policy.h
>> +++ b/libstdc++-v3/include/bits/hashtable_policy.h
>> @@ -41,8 +41,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>
>>   template<typename _Key, typename _Value, typename _Alloc,
>>        typename _ExtractKey, typename _Equal,
>> -       typename _H1, typename _H2, typename _Hash,
>> -       typename _RehashPolicy, typename _Traits>
>> +       typename _Hash, typename _RehashPolicy,
>> +       typename _Traits>
>>     class _Hashtable;
>>
>> namespace __detail
>> @@ -54,7 +54,8 @@ namespace __detail
>>    */
>>   template<typename _Key, typename _Value,
>>        typename _ExtractKey, typename _Equal,
>> -       typename _H1, typename _H2, typename _Hash, typename _Traits>
>> +       typename _Hash, typename _RangedHash,
>> +       typename _Traits>
>>     struct _Hashtable_base;
>>
>>   // Helper function: return distance(first, last) for forward
>> @@ -442,18 +443,12 @@ namespace __detail
>>     { return __num % __den; }
>>   };
>>
>> -  /// Default ranged hash function H.  In principle it should be a
>> -  /// function object composed from objects of type H1 and H2 such that
>> -  /// h(k, N) = h2(h1(k), N), but that would mean making extra 
>> copies of
>> -  /// h1 and h2.  So instead we'll just use a tag to tell class 
>> template
>> -  /// hashtable to do that composition.
>> -  struct _Default_ranged_hash { };
>
> We'd still need to keep this empty type, so that it's still used by
> the aliases like __umap_hashtable and still appears in mangled names.
>
> Does my suggestion make sense?
>
> I really like the general idea of getting rid of some of the
> complexity and not supporting infinite customization. But we can do
> that without changing mangled names of the _Hashtable specialiations.
>
>
I didn't thought we need to keep abi compatibility for extensions.

So yes, I'll do as you advised and re-submit the patch then.

François
Jonathan Wakely Aug. 6, 2020, 9:27 a.m. UTC | #8
On 06/08/20 08:35 +0200, François Dumont wrote:
>On 17/07/20 1:35 pm, Jonathan Wakely wrote:
>>I really like the general idea of getting rid of some of the
>>complexity and not supporting infinite customization. But we can do
>>that without changing mangled names of the _Hashtable specialiations.
>>
>>
>I didn't thought we need to keep abi compatibility for extensions.

These aren't extensions though, they're part of std::unordered_map
etc.

Just because something like _Vector_base is an internal type rather
than something defined in the standard doesn't mean we can just change
its ABI, because that would change the ABI of std::vector. It the same
here.

Changing _Hashtable affects all users of std::unordered_map etc.
François Dumont Aug. 17, 2020, 5:13 p.m. UTC | #9
Hi

     Here is the new proposal.

     As we can't remove template parameters I simply restore those that 
I tried to pass differently _H2 and _ExtractKey, so eventually I only 
remove usage of _Hash which I renamed in _Unused. Maybe I can keep the 
doc about it in hashtable.h and just add a remark saying that it is now 
unused.

     For _RangeHash, formerly _H2, and _ExtractKey I just stop 
maintaining any storage. When we need those I always use a value 
initialized instance. I kind of prefer the value initialization syntax 
because you can't confuse it with a function call but let me know if it 
is wrong and I should use _ExtractKey() or _RangeHash(). I also add some 
static assertions about those types regarding their noexcept qualifications.

     I also included in this patch the few changes left from [Hashtable 
0/6] which are mostly _M_insert_unique_node and _M_insert_multi_node 
signature cleanup as the key part can be extracted from the inserted node.

     Tested under Linux x86_64, ok to commit ?

François

On 06/08/20 11:27 am, Jonathan Wakely wrote:
> On 06/08/20 08:35 +0200, François Dumont wrote:
>> On 17/07/20 1:35 pm, Jonathan Wakely wrote:
>>> I really like the general idea of getting rid of some of the
>>> complexity and not supporting infinite customization. But we can do
>>> that without changing mangled names of the _Hashtable specialiations.
>>>
>>>
>> I didn't thought we need to keep abi compatibility for extensions.
>
> These aren't extensions though, they're part of std::unordered_map
> etc.
>
> Just because something like _Vector_base is an internal type rather
> than something defined in the standard doesn't mean we can just change
> its ABI, because that would change the ABI of std::vector. It the same
> here.
>
> Changing _Hashtable affects all users of std::unordered_map etc.
>
>
Jonathan Wakely Aug. 25, 2020, 2:30 p.m. UTC | #10
On 17/08/20 19:13 +0200, François Dumont via Libstdc++ wrote:
>Hi
>
>    Here is the new proposal.
>
>    As we can't remove template parameters I simply restore those that 
>I tried to pass differently _H2 and _ExtractKey, so eventually I only 
>remove usage of _Hash which I renamed in _Unused. Maybe I can keep the 
>doc about it in hashtable.h and just add a remark saying that it is 
>now unused.
>
>    For _RangeHash, formerly _H2, and _ExtractKey I just stop 
>maintaining any storage. When we need those I always use a value 
>initialized instance. I kind of prefer the value initialization syntax 
>because you can't confuse it with a function call but let me know if 
>it is wrong and I should use _ExtractKey() or _RangeHash(). I also add 
>some static assertions about those types regarding their noexcept 
>qualifications.
>
>    I also included in this patch the few changes left from [Hashtable 
>0/6] which are mostly _M_insert_unique_node and _M_insert_multi_node 
>signature cleanup as the key part can be extracted from the inserted 
>node.
>
>    Tested under Linux x86_64, ok to commit ?
>
>François
>
>On 06/08/20 11:27 am, Jonathan Wakely wrote:
>>On 06/08/20 08:35 +0200, François Dumont wrote:
>>>On 17/07/20 1:35 pm, Jonathan Wakely wrote:
>>>>I really like the general idea of getting rid of some of the
>>>>complexity and not supporting infinite customization. But we can do
>>>>that without changing mangled names of the _Hashtable specialiations.
>>>>
>>>>
>>>I didn't thought we need to keep abi compatibility for extensions.
>>
>>These aren't extensions though, they're part of std::unordered_map
>>etc.
>>
>>Just because something like _Vector_base is an internal type rather
>>than something defined in the standard doesn't mean we can just change
>>its ABI, because that would change the ABI of std::vector. It the same
>>here.
>>
>>Changing _Hashtable affects all users of std::unordered_map etc.
>>
>>
>

>diff --git a/libstdc++-v3/include/bits/hashtable.h b/libstdc++-v3/include/bits/hashtable.h
>index 7b772a475e3..1ba32a3c7e2 100644
>--- a/libstdc++-v3/include/bits/hashtable.h
>+++ b/libstdc++-v3/include/bits/hashtable.h
>@@ -311,35 +303,37 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 		    "Cache the hash code or qualify your functors involved"
> 		    " in hash code and bucket index computation with noexcept");
> 
>-      // When hash codes are cached local iterator inherits from H2 functor
>-      // which must then be default constructible.
>-      static_assert(__if_hash_cached<is_default_constructible<_H2>>::value,
>+      // To get bucket index we need _RangeHash not to throw.
>+      static_assert(is_nothrow_default_constructible<_RangeHash>::value,
> 		    "Functor used to map hash code to bucket index"
>-		    " must be default constructible");
>+		    " is nothrow default constructible");

Please phrase this as "must be nothrow default constructible".

>+      static_assert(noexcept(
>+	std::declval<const _RangeHash&>()((std::size_t)0, (std::size_t)0)),
>+		"Functor used to map hash code to bucket index is noexcept");

Same here, "must be noexcept".

Otherwise this looks great, thanks. Please push.
Jonathan Wakely Aug. 26, 2020, 3:30 p.m. UTC | #11
On 25/08/20 15:30 +0100, Jonathan Wakely wrote:
>On 17/08/20 19:13 +0200, François Dumont via Libstdc++ wrote:
>>Hi
>>
>>    Here is the new proposal.
>>
>>    As we can't remove template parameters I simply restore those 
>>that I tried to pass differently _H2 and _ExtractKey, so eventually 
>>I only remove usage of _Hash which I renamed in _Unused. Maybe I can 
>>keep the doc about it in hashtable.h and just add a remark saying 
>>that it is now unused.
>>
>>    For _RangeHash, formerly _H2, and _ExtractKey I just stop 
>>maintaining any storage. When we need those I always use a value 
>>initialized instance. I kind of prefer the value initialization 
>>syntax because you can't confuse it with a function call but let me 
>>know if it is wrong and I should use _ExtractKey() or _RangeHash(). 
>>I also add some static assertions about those types regarding their 
>>noexcept qualifications.
>>
>>    I also included in this patch the few changes left from 
>>[Hashtable 0/6] which are mostly _M_insert_unique_node and 
>>_M_insert_multi_node signature cleanup as the key part can be 
>>extracted from the inserted node.
>>
>>    Tested under Linux x86_64, ok to commit ?
>>
>>François
>>
>>On 06/08/20 11:27 am, Jonathan Wakely wrote:
>>>On 06/08/20 08:35 +0200, François Dumont wrote:
>>>>On 17/07/20 1:35 pm, Jonathan Wakely wrote:
>>>>>I really like the general idea of getting rid of some of the
>>>>>complexity and not supporting infinite customization. But we can do
>>>>>that without changing mangled names of the _Hashtable specialiations.
>>>>>
>>>>>
>>>>I didn't thought we need to keep abi compatibility for extensions.
>>>
>>>These aren't extensions though, they're part of std::unordered_map
>>>etc.
>>>
>>>Just because something like _Vector_base is an internal type rather
>>>than something defined in the standard doesn't mean we can just change
>>>its ABI, because that would change the ABI of std::vector. It the same
>>>here.
>>>
>>>Changing _Hashtable affects all users of std::unordered_map etc.
>>>
>>>
>>
>
>>diff --git a/libstdc++-v3/include/bits/hashtable.h b/libstdc++-v3/include/bits/hashtable.h
>>index 7b772a475e3..1ba32a3c7e2 100644
>>--- a/libstdc++-v3/include/bits/hashtable.h
>>+++ b/libstdc++-v3/include/bits/hashtable.h
>>@@ -311,35 +303,37 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>		    "Cache the hash code or qualify your functors involved"
>>		    " in hash code and bucket index computation with noexcept");
>>
>>-      // When hash codes are cached local iterator inherits from H2 functor
>>-      // which must then be default constructible.
>>-      static_assert(__if_hash_cached<is_default_constructible<_H2>>::value,
>>+      // To get bucket index we need _RangeHash not to throw.
>>+      static_assert(is_nothrow_default_constructible<_RangeHash>::value,
>>		    "Functor used to map hash code to bucket index"
>>-		    " must be default constructible");
>>+		    " is nothrow default constructible");
>
>Please phrase this as "must be nothrow default constructible".
>
>>+      static_assert(noexcept(
>>+	std::declval<const _RangeHash&>()((std::size_t)0, (std::size_t)0)),
>>+		"Functor used to map hash code to bucket index is noexcept");
>
>Same here, "must be noexcept".
>
>Otherwise this looks great, thanks. Please push.

I'm seeing new FAILures with this:

FAIL: 20_util/function_objects/searchers.cc (test for excess errors)
UNRESOLVED: 20_util/function_objects/searchers.cc compilation failed to produce executable
FAIL: experimental/functional/searchers.cc (test for excess errors)
UNRESOLVED: experimental/functional/searchers.cc compilation failed to produce executable

It looks like what you committed is not what you sent for review. The
patch sent for review has:

    /// Specialization: hash function and range-hashing function, no
    /// caching of hash codes.
    /// Provides typedef and accessor required by C++ 11.
    template<typename _Key, typename _Value, typename _ExtractKey,
-          typename _H1, typename _H2>
-    struct _Hash_code_base<_Key, _Value, _ExtractKey, _H1, _H2,
-                          _Default_ranged_hash, false>
+          typename _Hash, typename _RangeHash, typename _Unused>
+    struct _Hash_code_base<_Key, _Value, _ExtractKey, _Hash, _RangeHash,
+                          _Unused, false>
      : private _Hashtable_ebo_helper<0, _ExtractKey>,
-      private _Hashtable_ebo_helper<1, _H1>,
-      private _Hashtable_ebo_helper<2, _H2>
+      private _Hashtable_ebo_helper<1, _Hash>
      {


But what you committed has:

    /// Specialization: hash function and range-hashing function, no
    /// caching of hash codes.
    /// Provides typedef and accessor required by C++ 11.
    template<typename _Key, typename _Value, typename _ExtractKey,
-          typename _H1, typename _H2>
-    struct _Hash_code_base<_Key, _Value, _ExtractKey, _H1, _H2,
-                          _Default_ranged_hash, false>
-    : private _Hashtable_ebo_helper<0, _ExtractKey>,
-      private _Hashtable_ebo_helper<1, _H1>,
-      private _Hashtable_ebo_helper<2, _H2>
+          typename _Hash, typename _RangeHash, typename _Unused>
+    struct _Hash_code_base<_Key, _Value, _ExtractKey, _Hash, _RangeHash,
+                          _Unused, false>
+    : private _Hashtable_ebo_helper<0, _Hash>
      {


Note that you've changed the type of the base class from:

+      private _Hashtable_ebo_helper<1, _Hash>

to

+      private _Hashtable_ebo_helper<0, _Hash>

This causes an ambiguity:

/home/jwakely/src/gcc/build/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/hashtable_policy.h:1706: error: 'std::__detail::_Hashtable_ebo_helper<0, test03()::<unnamed struct>, true>' is an ambiguous base of 'std::__detail::_Hashtable_base<char, std::pair<const char, long int>, std::__detail::_Select1st, test03()::<unnamed struct>, test03()::<unnamed struct>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Hashtable_traits<true, false, true> >'

However, what I don't understand is why we are storing that _Hash type
more than once as a base class. That seems wrong (but not something we
can change without ABI impact).
Jonathan Wakely Aug. 26, 2020, 3:55 p.m. UTC | #12
On 26/08/20 16:30 +0100, Jonathan Wakely wrote:
>On 25/08/20 15:30 +0100, Jonathan Wakely wrote:
>>On 17/08/20 19:13 +0200, François Dumont via Libstdc++ wrote:
>>>Hi
>>>
>>>    Here is the new proposal.
>>>
>>>    As we can't remove template parameters I simply restore those 
>>>that I tried to pass differently _H2 and _ExtractKey, so 
>>>eventually I only remove usage of _Hash which I renamed in 
>>>_Unused. Maybe I can keep the doc about it in hashtable.h and just 
>>>add a remark saying that it is now unused.
>>>
>>>    For _RangeHash, formerly _H2, and _ExtractKey I just stop 
>>>maintaining any storage. When we need those I always use a value 
>>>initialized instance. I kind of prefer the value initialization 
>>>syntax because you can't confuse it with a function call but let 
>>>me know if it is wrong and I should use _ExtractKey() or 
>>>_RangeHash(). I also add some static assertions about those types 
>>>regarding their noexcept qualifications.
>>>
>>>    I also included in this patch the few changes left from 
>>>[Hashtable 0/6] which are mostly _M_insert_unique_node and 
>>>_M_insert_multi_node signature cleanup as the key part can be 
>>>extracted from the inserted node.
>>>
>>>    Tested under Linux x86_64, ok to commit ?
>>>
>>>François
>>>
>>>On 06/08/20 11:27 am, Jonathan Wakely wrote:
>>>>On 06/08/20 08:35 +0200, François Dumont wrote:
>>>>>On 17/07/20 1:35 pm, Jonathan Wakely wrote:
>>>>>>I really like the general idea of getting rid of some of the
>>>>>>complexity and not supporting infinite customization. But we can do
>>>>>>that without changing mangled names of the _Hashtable specialiations.
>>>>>>
>>>>>>
>>>>>I didn't thought we need to keep abi compatibility for extensions.
>>>>
>>>>These aren't extensions though, they're part of std::unordered_map
>>>>etc.
>>>>
>>>>Just because something like _Vector_base is an internal type rather
>>>>than something defined in the standard doesn't mean we can just change
>>>>its ABI, because that would change the ABI of std::vector. It the same
>>>>here.
>>>>
>>>>Changing _Hashtable affects all users of std::unordered_map etc.
>>>>
>>>>
>>>
>>
>>>diff --git a/libstdc++-v3/include/bits/hashtable.h b/libstdc++-v3/include/bits/hashtable.h
>>>index 7b772a475e3..1ba32a3c7e2 100644
>>>--- a/libstdc++-v3/include/bits/hashtable.h
>>>+++ b/libstdc++-v3/include/bits/hashtable.h
>>>@@ -311,35 +303,37 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>>		    "Cache the hash code or qualify your functors involved"
>>>		    " in hash code and bucket index computation with noexcept");
>>>
>>>-      // When hash codes are cached local iterator inherits from H2 functor
>>>-      // which must then be default constructible.
>>>-      static_assert(__if_hash_cached<is_default_constructible<_H2>>::value,
>>>+      // To get bucket index we need _RangeHash not to throw.
>>>+      static_assert(is_nothrow_default_constructible<_RangeHash>::value,
>>>		    "Functor used to map hash code to bucket index"
>>>-		    " must be default constructible");
>>>+		    " is nothrow default constructible");
>>
>>Please phrase this as "must be nothrow default constructible".
>>
>>>+      static_assert(noexcept(
>>>+	std::declval<const _RangeHash&>()((std::size_t)0, (std::size_t)0)),
>>>+		"Functor used to map hash code to bucket index is noexcept");
>>
>>Same here, "must be noexcept".
>>
>>Otherwise this looks great, thanks. Please push.
>
>I'm seeing new FAILures with this:
>
>FAIL: 20_util/function_objects/searchers.cc (test for excess errors)
>UNRESOLVED: 20_util/function_objects/searchers.cc compilation failed to produce executable
>FAIL: experimental/functional/searchers.cc (test for excess errors)
>UNRESOLVED: experimental/functional/searchers.cc compilation failed to produce executable
>
>It looks like what you committed is not what you sent for review. The
>patch sent for review has:
>
>   /// Specialization: hash function and range-hashing function, no
>   /// caching of hash codes.
>   /// Provides typedef and accessor required by C++ 11.
>   template<typename _Key, typename _Value, typename _ExtractKey,
>-          typename _H1, typename _H2>
>-    struct _Hash_code_base<_Key, _Value, _ExtractKey, _H1, _H2,
>-                          _Default_ranged_hash, false>
>+          typename _Hash, typename _RangeHash, typename _Unused>
>+    struct _Hash_code_base<_Key, _Value, _ExtractKey, _Hash, _RangeHash,
>+                          _Unused, false>
>     : private _Hashtable_ebo_helper<0, _ExtractKey>,
>-      private _Hashtable_ebo_helper<1, _H1>,
>-      private _Hashtable_ebo_helper<2, _H2>
>+      private _Hashtable_ebo_helper<1, _Hash>
>     {
>
>
>But what you committed has:
>
>   /// Specialization: hash function and range-hashing function, no
>   /// caching of hash codes.
>   /// Provides typedef and accessor required by C++ 11.
>   template<typename _Key, typename _Value, typename _ExtractKey,
>-          typename _H1, typename _H2>
>-    struct _Hash_code_base<_Key, _Value, _ExtractKey, _H1, _H2,
>-                          _Default_ranged_hash, false>
>-    : private _Hashtable_ebo_helper<0, _ExtractKey>,
>-      private _Hashtable_ebo_helper<1, _H1>,
>-      private _Hashtable_ebo_helper<2, _H2>
>+          typename _Hash, typename _RangeHash, typename _Unused>
>+    struct _Hash_code_base<_Key, _Value, _ExtractKey, _Hash, _RangeHash,
>+                          _Unused, false>
>+    : private _Hashtable_ebo_helper<0, _Hash>
>     {
>
>
>Note that you've changed the type of the base class from:
>
>+      private _Hashtable_ebo_helper<1, _Hash>
>
>to
>
>+      private _Hashtable_ebo_helper<0, _Hash>
>
>This causes an ambiguity:
>
>/home/jwakely/src/gcc/build/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/hashtable_policy.h:1706: error: 'std::__detail::_Hashtable_ebo_helper<0, test03()::<unnamed struct>, true>' is an ambiguous base of 'std::__detail::_Hashtable_base<char, std::pair<const char, long int>, std::__detail::_Select1st, test03()::<unnamed struct>, test03()::<unnamed struct>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Hashtable_traits<true, false, true> >'
>
>However, what I don't understand is why we are storing that _Hash type
>more than once as a base class. That seems wrong (but not something we
>can change without ABI impact).

Ah, we're not storing it more than once.

The problem is:

   template<typename _Key, typename _Value,
	   typename _ExtractKey, typename _Equal,
	   typename _H1, typename _H2, typename _Hash, typename _Traits>
   struct _Hashtable_base
   : public _Hash_code_base<_Key, _Value, _ExtractKey, _H1, _H2, _Hash,
			   _Traits::__hash_cached::value>,
     private _Hashtable_ebo_helper<0, _Equal>

This has a base of _Hashtable_ebo_helper<0, _Equal> so it used to
have these bases:

_Hashtable_ebo_helper<0, _ExtractKey>
_Hashtable_ebo_helper<1, _Hash>
_Hashtable_ebo_helper<2, _RangeHash>
_Hashtable_ebo_helper<0, _Equal>

but after your change it has these bases:

_Hashtable_ebo_helper<0, _Hash>
_Hashtable_ebo_helper<0, _Equal>

In the case
where _Equal and _Hash are the same type (which is what I was testing
in the test that fail, because I'm sneaky) that means:

_Hashtable_ebo_helper<0, T>
_Hashtable_ebo_helper<0, T>

which is obviously ambiguous.

I think the _hash_code_base should still use the index 1 for its base
class, i.e. _Hashtable_ebo_helper<1, _Hash>. That way we have these:

_Hashtable_ebo_helper<1, _Hash>
_Hashtable_ebo_helper<0, _Equal>

which works even if they're the same types.
Jonathan Wakely Aug. 26, 2020, 3:58 p.m. UTC | #13
On 26/08/20 16:55 +0100, Jonathan Wakely wrote:
>On 26/08/20 16:30 +0100, Jonathan Wakely wrote:
>>I'm seeing new FAILures with this:
>>
>>FAIL: 20_util/function_objects/searchers.cc (test for excess errors)
>>UNRESOLVED: 20_util/function_objects/searchers.cc compilation failed to produce executable
>>FAIL: experimental/functional/searchers.cc (test for excess errors)
>>UNRESOLVED: experimental/functional/searchers.cc compilation failed to produce executable
>>
>>It looks like what you committed is not what you sent for review. The
>>patch sent for review has:
>>
>>  /// Specialization: hash function and range-hashing function, no
>>  /// caching of hash codes.
>>  /// Provides typedef and accessor required by C++ 11.
>>  template<typename _Key, typename _Value, typename _ExtractKey,
>>-          typename _H1, typename _H2>
>>-    struct _Hash_code_base<_Key, _Value, _ExtractKey, _H1, _H2,
>>-                          _Default_ranged_hash, false>
>>+          typename _Hash, typename _RangeHash, typename _Unused>
>>+    struct _Hash_code_base<_Key, _Value, _ExtractKey, _Hash, _RangeHash,
>>+                          _Unused, false>
>>    : private _Hashtable_ebo_helper<0, _ExtractKey>,
>>-      private _Hashtable_ebo_helper<1, _H1>,
>>-      private _Hashtable_ebo_helper<2, _H2>
>>+      private _Hashtable_ebo_helper<1, _Hash>
>>    {
>>
>>
>>But what you committed has:
>>
>>  /// Specialization: hash function and range-hashing function, no
>>  /// caching of hash codes.
>>  /// Provides typedef and accessor required by C++ 11.
>>  template<typename _Key, typename _Value, typename _ExtractKey,
>>-          typename _H1, typename _H2>
>>-    struct _Hash_code_base<_Key, _Value, _ExtractKey, _H1, _H2,
>>-                          _Default_ranged_hash, false>
>>-    : private _Hashtable_ebo_helper<0, _ExtractKey>,
>>-      private _Hashtable_ebo_helper<1, _H1>,
>>-      private _Hashtable_ebo_helper<2, _H2>
>>+          typename _Hash, typename _RangeHash, typename _Unused>
>>+    struct _Hash_code_base<_Key, _Value, _ExtractKey, _Hash, _RangeHash,
>>+                          _Unused, false>
>>+    : private _Hashtable_ebo_helper<0, _Hash>
>>    {
>>
>>
>>Note that you've changed the type of the base class from:
>>
>>+      private _Hashtable_ebo_helper<1, _Hash>
>>
>>to
>>
>>+      private _Hashtable_ebo_helper<0, _Hash>
>>
>>This causes an ambiguity:
>>
>>/home/jwakely/src/gcc/build/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/hashtable_policy.h:1706: error: 'std::__detail::_Hashtable_ebo_helper<0, test03()::<unnamed struct>, true>' is an ambiguous base of 'std::__detail::_Hashtable_base<char, std::pair<const char, long int>, std::__detail::_Select1st, test03()::<unnamed struct>, test03()::<unnamed struct>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Hashtable_traits<true, false, true> >'
>>
>>However, what I don't understand is why we are storing that _Hash type
>>more than once as a base class. That seems wrong (but not something we
>>can change without ABI impact).
>
>Ah, we're not storing it more than once.
>
>The problem is:
>
>  template<typename _Key, typename _Value,
>	   typename _ExtractKey, typename _Equal,
>	   typename _H1, typename _H2, typename _Hash, typename _Traits>
>  struct _Hashtable_base
>  : public _Hash_code_base<_Key, _Value, _ExtractKey, _H1, _H2, _Hash,
>			   _Traits::__hash_cached::value>,
>    private _Hashtable_ebo_helper<0, _Equal>
>
>This has a base of _Hashtable_ebo_helper<0, _Equal> so it used to
>have these bases:
>
>_Hashtable_ebo_helper<0, _ExtractKey>
>_Hashtable_ebo_helper<1, _Hash>
>_Hashtable_ebo_helper<2, _RangeHash>
>_Hashtable_ebo_helper<0, _Equal>
>
>but after your change it has these bases:
>
>_Hashtable_ebo_helper<0, _Hash>
>_Hashtable_ebo_helper<0, _Equal>
>
>In the case
>where _Equal and _Hash are the same type (which is what I was testing
>in the test that fail, because I'm sneaky) that means:
>
>_Hashtable_ebo_helper<0, T>
>_Hashtable_ebo_helper<0, T>
>
>which is obviously ambiguous.
>
>I think the _hash_code_base should still use the index 1 for its base
>class, i.e. _Hashtable_ebo_helper<1, _Hash>. That way we have these:
>
>_Hashtable_ebo_helper<1, _Hash>
>_Hashtable_ebo_helper<0, _Equal>
>
>which works even if they're the same types.

Here's a minimal test:

#include <unordered_map>

struct Evil : std::hash<int>, std::equal_to<int>
{
};

int main()
{
   std::unordered_map<int, int, Evil, Evil> h;
   h.key_eq();
}

This fails on current trunk.
Jonathan Wakely Aug. 26, 2020, 4:45 p.m. UTC | #14
On 26/08/20 16:58 +0100, Jonathan Wakely wrote:
>On 26/08/20 16:55 +0100, Jonathan Wakely wrote:
>>On 26/08/20 16:30 +0100, Jonathan Wakely wrote:
>>>I'm seeing new FAILures with this:
>>>
>>>FAIL: 20_util/function_objects/searchers.cc (test for excess errors)
>>>UNRESOLVED: 20_util/function_objects/searchers.cc compilation failed to produce executable
>>>FAIL: experimental/functional/searchers.cc (test for excess errors)
>>>UNRESOLVED: experimental/functional/searchers.cc compilation failed to produce executable
>>>
>>>It looks like what you committed is not what you sent for review. The
>>>patch sent for review has:
>>>
>>> /// Specialization: hash function and range-hashing function, no
>>> /// caching of hash codes.
>>> /// Provides typedef and accessor required by C++ 11.
>>> template<typename _Key, typename _Value, typename _ExtractKey,
>>>-          typename _H1, typename _H2>
>>>-    struct _Hash_code_base<_Key, _Value, _ExtractKey, _H1, _H2,
>>>-                          _Default_ranged_hash, false>
>>>+          typename _Hash, typename _RangeHash, typename _Unused>
>>>+    struct _Hash_code_base<_Key, _Value, _ExtractKey, _Hash, _RangeHash,
>>>+                          _Unused, false>
>>>   : private _Hashtable_ebo_helper<0, _ExtractKey>,
>>>-      private _Hashtable_ebo_helper<1, _H1>,
>>>-      private _Hashtable_ebo_helper<2, _H2>
>>>+      private _Hashtable_ebo_helper<1, _Hash>
>>>   {
>>>
>>>
>>>But what you committed has:
>>>
>>> /// Specialization: hash function and range-hashing function, no
>>> /// caching of hash codes.
>>> /// Provides typedef and accessor required by C++ 11.
>>> template<typename _Key, typename _Value, typename _ExtractKey,
>>>-          typename _H1, typename _H2>
>>>-    struct _Hash_code_base<_Key, _Value, _ExtractKey, _H1, _H2,
>>>-                          _Default_ranged_hash, false>
>>>-    : private _Hashtable_ebo_helper<0, _ExtractKey>,
>>>-      private _Hashtable_ebo_helper<1, _H1>,
>>>-      private _Hashtable_ebo_helper<2, _H2>
>>>+          typename _Hash, typename _RangeHash, typename _Unused>
>>>+    struct _Hash_code_base<_Key, _Value, _ExtractKey, _Hash, _RangeHash,
>>>+                          _Unused, false>
>>>+    : private _Hashtable_ebo_helper<0, _Hash>
>>>   {
>>>
>>>
>>>Note that you've changed the type of the base class from:
>>>
>>>+      private _Hashtable_ebo_helper<1, _Hash>
>>>
>>>to
>>>
>>>+      private _Hashtable_ebo_helper<0, _Hash>
>>>
>>>This causes an ambiguity:
>>>
>>>/home/jwakely/src/gcc/build/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/hashtable_policy.h:1706: error: 'std::__detail::_Hashtable_ebo_helper<0, test03()::<unnamed struct>, true>' is an ambiguous base of 'std::__detail::_Hashtable_base<char, std::pair<const char, long int>, std::__detail::_Select1st, test03()::<unnamed struct>, test03()::<unnamed struct>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Hashtable_traits<true, false, true> >'
>>>
>>>However, what I don't understand is why we are storing that _Hash type
>>>more than once as a base class. That seems wrong (but not something we
>>>can change without ABI impact).
>>
>>Ah, we're not storing it more than once.
>>
>>The problem is:
>>
>> template<typename _Key, typename _Value,
>>	   typename _ExtractKey, typename _Equal,
>>	   typename _H1, typename _H2, typename _Hash, typename _Traits>
>> struct _Hashtable_base
>> : public _Hash_code_base<_Key, _Value, _ExtractKey, _H1, _H2, _Hash,
>>			   _Traits::__hash_cached::value>,
>>   private _Hashtable_ebo_helper<0, _Equal>
>>
>>This has a base of _Hashtable_ebo_helper<0, _Equal> so it used to
>>have these bases:
>>
>>_Hashtable_ebo_helper<0, _ExtractKey>
>>_Hashtable_ebo_helper<1, _Hash>
>>_Hashtable_ebo_helper<2, _RangeHash>
>>_Hashtable_ebo_helper<0, _Equal>
>>
>>but after your change it has these bases:
>>
>>_Hashtable_ebo_helper<0, _Hash>
>>_Hashtable_ebo_helper<0, _Equal>
>>
>>In the case
>>where _Equal and _Hash are the same type (which is what I was testing
>>in the test that fail, because I'm sneaky) that means:
>>
>>_Hashtable_ebo_helper<0, T>
>>_Hashtable_ebo_helper<0, T>
>>
>>which is obviously ambiguous.
>>
>>I think the _hash_code_base should still use the index 1 for its base
>>class, i.e. _Hashtable_ebo_helper<1, _Hash>. That way we have these:
>>
>>_Hashtable_ebo_helper<1, _Hash>
>>_Hashtable_ebo_helper<0, _Equal>
>>
>>which works even if they're the same types.
>
>Here's a minimal test:
>
>#include <unordered_map>
>
>struct Evil : std::hash<int>, std::equal_to<int>
>{
>};
>
>int main()
>{
>  std::unordered_map<int, int, Evil, Evil> h;
>  h.key_eq();
>}
>
>This fails on current trunk.

Fixed by the attached patch.

Tested powerpc64le-linux, committed to trunk.
François Dumont Aug. 26, 2020, 8:37 p.m. UTC | #15
Deeply sorry, I indeed didn't sent the patch I wanted to commit. It was 
in 3 commits on my side and it looks like I had miss the last one 
regarding the changes for _ExtractKey.

But moreover I had change the ebo helper index wrongly, I missed the abi 
change here, thanks for fixing it.


On 26/08/20 6:45 pm, Jonathan Wakely wrote:
> On 26/08/20 16:58 +0100, Jonathan Wakely wrote:
>> On 26/08/20 16:55 +0100, Jonathan Wakely wrote:
>>> On 26/08/20 16:30 +0100, Jonathan Wakely wrote:
>>>> I'm seeing new FAILures with this:
>>>>
>>>> FAIL: 20_util/function_objects/searchers.cc (test for excess errors)
>>>> UNRESOLVED: 20_util/function_objects/searchers.cc compilation 
>>>> failed to produce executable
>>>> FAIL: experimental/functional/searchers.cc (test for excess errors)
>>>> UNRESOLVED: experimental/functional/searchers.cc compilation failed 
>>>> to produce executable
>>>>
>>>> It looks like what you committed is not what you sent for review. The
>>>> patch sent for review has:
>>>>
>>>> /// Specialization: hash function and range-hashing function, no
>>>> /// caching of hash codes.
>>>> /// Provides typedef and accessor required by C++ 11.
>>>> template<typename _Key, typename _Value, typename _ExtractKey,
>>>> -          typename _H1, typename _H2>
>>>> -    struct _Hash_code_base<_Key, _Value, _ExtractKey, _H1, _H2,
>>>> -                          _Default_ranged_hash, false>
>>>> +          typename _Hash, typename _RangeHash, typename _Unused>
>>>> +    struct _Hash_code_base<_Key, _Value, _ExtractKey, _Hash, 
>>>> _RangeHash,
>>>> +                          _Unused, false>
>>>>   : private _Hashtable_ebo_helper<0, _ExtractKey>,
>>>> -      private _Hashtable_ebo_helper<1, _H1>,
>>>> -      private _Hashtable_ebo_helper<2, _H2>
>>>> +      private _Hashtable_ebo_helper<1, _Hash>
>>>>   {
>>>>
>>>>
>>>> But what you committed has:
>>>>
>>>> /// Specialization: hash function and range-hashing function, no
>>>> /// caching of hash codes.
>>>> /// Provides typedef and accessor required by C++ 11.
>>>> template<typename _Key, typename _Value, typename _ExtractKey,
>>>> -          typename _H1, typename _H2>
>>>> -    struct _Hash_code_base<_Key, _Value, _ExtractKey, _H1, _H2,
>>>> -                          _Default_ranged_hash, false>
>>>> -    : private _Hashtable_ebo_helper<0, _ExtractKey>,
>>>> -      private _Hashtable_ebo_helper<1, _H1>,
>>>> -      private _Hashtable_ebo_helper<2, _H2>
>>>> +          typename _Hash, typename _RangeHash, typename _Unused>
>>>> +    struct _Hash_code_base<_Key, _Value, _ExtractKey, _Hash, 
>>>> _RangeHash,
>>>> +                          _Unused, false>
>>>> +    : private _Hashtable_ebo_helper<0, _Hash>
>>>>   {
>>>>
>>>>
>>>> Note that you've changed the type of the base class from:
>>>>
>>>> +      private _Hashtable_ebo_helper<1, _Hash>
>>>>
>>>> to
>>>>
>>>> +      private _Hashtable_ebo_helper<0, _Hash>
>>>>
>>>> This causes an ambiguity:
>>>>
>>>> /home/jwakely/src/gcc/build/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/hashtable_policy.h:1706: 
>>>> error: 'std::__detail::_Hashtable_ebo_helper<0, test03()::<unnamed 
>>>> struct>, true>' is an ambiguous base of 
>>>> 'std::__detail::_Hashtable_base<char, std::pair<const char, long 
>>>> int>, std::__detail::_Select1st, test03()::<unnamed struct>, 
>>>> test03()::<unnamed struct>, std::__detail::_Mod_range_hashing, 
>>>> std::__detail::_Default_ranged_hash, 
>>>> std::__detail::_Hashtable_traits<true, false, true> >'
>>>>
>>>> However, what I don't understand is why we are storing that _Hash type
>>>> more than once as a base class. That seems wrong (but not something we
>>>> can change without ABI impact).
>>>
>>> Ah, we're not storing it more than once.
>>>
>>> The problem is:
>>>
>>> template<typename _Key, typename _Value,
>>>        typename _ExtractKey, typename _Equal,
>>>        typename _H1, typename _H2, typename _Hash, typename _Traits>
>>> struct _Hashtable_base
>>> : public _Hash_code_base<_Key, _Value, _ExtractKey, _H1, _H2, _Hash,
>>>                _Traits::__hash_cached::value>,
>>>   private _Hashtable_ebo_helper<0, _Equal>
>>>
>>> This has a base of _Hashtable_ebo_helper<0, _Equal> so it used to
>>> have these bases:
>>>
>>> _Hashtable_ebo_helper<0, _ExtractKey>
>>> _Hashtable_ebo_helper<1, _Hash>
>>> _Hashtable_ebo_helper<2, _RangeHash>
>>> _Hashtable_ebo_helper<0, _Equal>
>>>
>>> but after your change it has these bases:
>>>
>>> _Hashtable_ebo_helper<0, _Hash>
>>> _Hashtable_ebo_helper<0, _Equal>
>>>
>>> In the case
>>> where _Equal and _Hash are the same type (which is what I was testing
>>> in the test that fail, because I'm sneaky) that means:
>>>
>>> _Hashtable_ebo_helper<0, T>
>>> _Hashtable_ebo_helper<0, T>
>>>
>>> which is obviously ambiguous.
>>>
>>> I think the _hash_code_base should still use the index 1 for its base
>>> class, i.e. _Hashtable_ebo_helper<1, _Hash>. That way we have these:
>>>
>>> _Hashtable_ebo_helper<1, _Hash>
>>> _Hashtable_ebo_helper<0, _Equal>
>>>
>>> which works even if they're the same types.
>>
>> Here's a minimal test:
>>
>> #include <unordered_map>
>>
>> struct Evil : std::hash<int>, std::equal_to<int>
>> {
>> };
>>
>> int main()
>> {
>>  std::unordered_map<int, int, Evil, Evil> h;
>>  h.key_eq();
>> }
>>
>> This fails on current trunk.
>
> Fixed by the attached patch.
>
> Tested powerpc64le-linux, committed to trunk.
>
>
diff mbox series

Patch

diff --git a/libstdc++-v3/include/bits/hashtable.h b/libstdc++-v3/include/bits/hashtable.h
index ad07a36eb83..d09c851e8a4 100644
--- a/libstdc++-v3/include/bits/hashtable.h
+++ b/libstdc++-v3/include/bits/hashtable.h
@@ -69,31 +69,18 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
    *  and returns a bool-like value that is true if the two objects
    *  are considered equal.
    *
-   *  @tparam _H1  The hash function. A unary function object with
+   *  @tparam _Hash  The hash function. A unary function object with
    *  argument type _Key and result type size_t. Return values should
    *  be distributed over the entire range [0, numeric_limits<size_t>:::max()].
    *
-   *  @tparam _H2  The range-hashing function (in the terminology of
-   *  Tavori and Dreizin).  A binary function object whose argument
-   *  types and result type are all size_t.  Given arguments r and N,
-   *  the return value is in the range [0, N).
-   *
-   *  @tparam _Hash  The ranged hash function (Tavori and Dreizin). A
-   *  binary function whose argument types are _Key and size_t and
-   *  whose result type is size_t.  Given arguments k and N, the
-   *  return value is in the range [0, N).  Default: hash(k, N) =
-   *  h2(h1(k), N).  If _Hash is anything other than the default, _H1
-   *  and _H2 are ignored.
-   *
-   *  @tparam _RehashPolicy  Policy class with three members, all of
-   *  which govern the bucket count. _M_next_bkt(n) returns a bucket
-   *  count no smaller than n.  _M_bkt_for_elements(n) returns a
-   *  bucket count appropriate for an element count of n.
-   *  _M_need_rehash(n_bkt, n_elt, n_ins) determines whether, if the
-   *  current bucket count is n_bkt and the current element count is
-   *  n_elt, we need to increase the bucket count.  If so, returns
-   *  make_pair(true, n), where n is the new bucket count.  If not,
-   *  returns make_pair(false, <anything>)
+   *  @tparam _RehashPolicy  Policy class with three members, all of which
+   *  govern the bucket count. _M_next_bkt(n) returns a bucket count no smaller
+   *  than n. _M_bkt_for_elements(n) returns a bucket count appropriate for an
+   *  element count of n. _M_need_rehash(n_bkt, n_elt, n_ins) determines
+   *  whether, if the current bucket count is n_bkt and the current element
+   *  count is n_elt, we need to increase the bucket count for n_ins insertions.
+   *  If so, returns make_pair(true, n), where n is the new bucket count. If
+   *  not, returns make_pair(false, <anything>)
    *
    *  @tparam _Traits  Compile-time class with three boolean
    *  std::integral_constant members:  __cache_hash_code, __constant_iterators,
@@ -168,19 +155,19 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
    */
   template<typename _Key, typename _Value, typename _Alloc,
 	   typename _ExtractKey, typename _Equal,
-	   typename _H1, typename _H2, typename _Hash,
-	   typename _RehashPolicy, typename _Traits>
+	   typename _Hash, typename _RehashPolicy, typename _Traits>
     class _Hashtable
-    : public __detail::_Hashtable_base<_Key, _Value, _ExtractKey, _Equal,
-				       _H1, _H2, _Hash, _Traits>,
+    : public __detail::_Hashtable_base<_Key, _Value, _ExtractKey, _Equal, _Hash,
+				       typename _RehashPolicy::__ranged_hash,
+				       _Traits>,
       public __detail::_Map_base<_Key, _Value, _Alloc, _ExtractKey, _Equal,
-				 _H1, _H2, _Hash, _RehashPolicy, _Traits>,
+				 _Hash, _RehashPolicy, _Traits>,
       public __detail::_Insert<_Key, _Value, _Alloc, _ExtractKey, _Equal,
-			       _H1, _H2, _Hash, _RehashPolicy, _Traits>,
+			       _Hash, _RehashPolicy, _Traits>,
       public __detail::_Rehash_base<_Key, _Value, _Alloc, _ExtractKey, _Equal,
-				    _H1, _H2, _Hash, _RehashPolicy, _Traits>,
+				    _Hash, _RehashPolicy, _Traits>,
       public __detail::_Equality<_Key, _Value, _Alloc, _ExtractKey, _Equal,
-				 _H1, _H2, _Hash, _RehashPolicy, _Traits>,
+				 _Hash, _RehashPolicy, _Traits>,
       private __detail::_Hashtable_alloc<
 	__alloc_rebind<_Alloc,
 		       __detail::_Hash_node<_Value,
@@ -235,26 +222,26 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 				       	     __detail::_Identity,
 					     __detail::_Select1st>::type;
 
-      using __hashtable_base = __detail::
-			       _Hashtable_base<_Key, _Value, _ExtractKey,
-					      _Equal, _H1, _H2, _Hash, _Traits>;
+      using __hashtable_base =
+	__detail::_Hashtable_base<_Key, _Value, _ExtractKey, _Equal,
+				  _Hash, typename _RehashPolicy::__ranged_hash,
+				  _Traits>;
 
       using __hash_code_base =  typename __hashtable_base::__hash_code_base;
       using __hash_code =  typename __hashtable_base::__hash_code;
       using __ireturn_type = typename __hashtable_base::__ireturn_type;
 
-      using __map_base = __detail::_Map_base<_Key, _Value, _Alloc, _ExtractKey,
-					     _Equal, _H1, _H2, _Hash,
-					     _RehashPolicy, _Traits>;
+      using __map_base =
+	__detail::_Map_base<_Key, _Value, _Alloc, _ExtractKey, _Equal,
+			    _Hash, _RehashPolicy, _Traits>;
 
-      using __rehash_base = __detail::_Rehash_base<_Key, _Value, _Alloc,
-						   _ExtractKey, _Equal,
-						   _H1, _H2, _Hash,
-						   _RehashPolicy, _Traits>;
+      using __rehash_base =
+	__detail::_Rehash_base<_Key, _Value, _Alloc, _ExtractKey, _Equal,
+			       _Hash, _RehashPolicy, _Traits>;
 
-      using __eq_base = __detail::_Equality<_Key, _Value, _Alloc, _ExtractKey,
-					    _Equal, _H1, _H2, _Hash,
-					    _RehashPolicy, _Traits>;
+      using __eq_base =
+	__detail::_Equality<_Key, _Value, _Alloc, _ExtractKey, _Equal,
+			    _Hash, _RehashPolicy, _Traits>;
 
       using __reuse_or_alloc_node_gen_t =
 	__detail::_ReuseOrAllocNode<__node_alloc_type>;
@@ -321,29 +308,20 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 		    "Cache the hash code or qualify your functors involved"
 		    " in hash code and bucket index computation with noexcept");
 
-      // When hash codes are cached local iterator inherits from H2 functor
-      // which must then be default constructible.
-      static_assert(__if_hash_cached<is_default_constructible<_H2>>::value,
-		    "Functor used to map hash code to bucket index"
-		    " must be default constructible");
-
       template<typename _Keya, typename _Valuea, typename _Alloca,
 	       typename _ExtractKeya, typename _Equala,
-	       typename _H1a, typename _H2a, typename _Hasha,
-	       typename _RehashPolicya, typename _Traitsa,
+	       typename _Hasha, typename _RehashPolicya, typename _Traitsa,
 	       bool _Unique_keysa>
 	friend struct __detail::_Map_base;
 
       template<typename _Keya, typename _Valuea, typename _Alloca,
 	       typename _ExtractKeya, typename _Equala,
-	       typename _H1a, typename _H2a, typename _Hasha,
-	       typename _RehashPolicya, typename _Traitsa>
+	       typename _Hasha, typename _RehashPolicya, typename _Traitsa>
 	friend struct __detail::_Insert_base;
 
       template<typename _Keya, typename _Valuea, typename _Alloca,
 	       typename _ExtractKeya, typename _Equala,
-	       typename _H1a, typename _H2a, typename _Hasha,
-	       typename _RehashPolicya, typename _Traitsa,
+	       typename _Hasha, typename _RehashPolicya, typename _Traitsa,
 	       bool _Constant_iteratorsa>
 	friend struct __detail::_Insert;
 
@@ -456,15 +434,14 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
       void
       _M_reset() noexcept;
 
-      _Hashtable(const _H1& __h1, const _H2& __h2, const _Hash& __h,
-		 const _Equal& __eq, const _ExtractKey& __exk,
+      _Hashtable(const _Hash& __h, const _Equal& __eq, const _ExtractKey& __exk,
 		 const allocator_type& __a)
-      : __hashtable_base(__exk, __h1, __h2, __h, __eq),
+      : __hashtable_base(__exk, __h, __eq),
 	__hashtable_alloc(__node_alloc_type(__a))
       { }
 
       _Hashtable(_Hashtable&& __ht, __node_alloc_type&& __a, true_type)
-	noexcept(std::is_nothrow_copy_constructible<_H1>::value &&
+	noexcept(std::is_nothrow_copy_constructible<_Hash>::value &&
 		 std::is_nothrow_copy_constructible<_Equal>::value)
       : __hashtable_base(__ht),
 	__map_base(__ht),
@@ -495,16 +472,14 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
       template<typename _InputIterator>
 	_Hashtable(_InputIterator __first, _InputIterator __last,
 		   size_type __bkt_count_hint,
-		   const _H1&, const _H2&, const _Hash&,
-		   const _Equal&, const _ExtractKey&,
+		   const _Hash&, const _Equal&, const _ExtractKey&,
 		   const allocator_type&,
 		   __unique_keys_t);
 
       template<typename _InputIterator>
 	_Hashtable(_InputIterator __first, _InputIterator __last,
 		   size_type __bkt_count_hint,
-		   const _H1&, const _H2&, const _Hash&,
-		   const _Equal&, const _ExtractKey&,
+		   const _Hash&, const _Equal&, const _ExtractKey&,
 		   const allocator_type&,
 		   __multi_keys_t);
 
@@ -512,8 +487,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
       // Constructor, destructor, assignment, swap
       _Hashtable() = default;
       _Hashtable(size_type __bkt_count_hint,
-		 const _H1&, const _H2&, const _Hash&,
-		 const _Equal&, const _ExtractKey&,
+		 const _Hash&, const _Equal&, const _ExtractKey&,
 		 const allocator_type&);
 
       _Hashtable(const _Hashtable&);
@@ -541,11 +515,10 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
       template<typename _InputIterator>
 	_Hashtable(_InputIterator __first, _InputIterator __last,
 		   size_type __bkt_count_hint,
-		   const _H1& __h1, const _H2& __h2, const _Hash& __h,
-		   const _Equal& __eq, const _ExtractKey& __exk,
-		   const allocator_type& __a)
+		   const _Hash& __h, const _Equal& __eq,
+		   const _ExtractKey& __exk, const allocator_type& __a)
 	: _Hashtable(__first, __last, __bkt_count_hint,
-		     __h1, __h2, __h, __eq, __exk, __a, __unique_keys{})
+		     __h, __eq, __exk, __a, __unique_keys{})
 	{ }
 
       explicit
@@ -555,31 +528,29 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       explicit
       _Hashtable(size_type __bkt_count_hint,
-		 const _H1& __hf = _H1(),
+		 const _Hash& __hf = _Hash(),
 		 const key_equal& __eql = key_equal(),
 		 const allocator_type& __a = allocator_type())
-      : _Hashtable(__bkt_count_hint, __hf, _H2(), _Hash(), __eql,
-		   __key_extract(), __a)
+      : _Hashtable(__bkt_count_hint, __hf, __eql, __key_extract(), __a)
       { }
 
       template<typename _InputIterator>
 	_Hashtable(_InputIterator __f, _InputIterator __l,
 		   size_type __bkt_count_hint = 0,
-		   const _H1& __hf = _H1(),
+		   const _Hash& __hf = _Hash(),
 		   const key_equal& __eql = key_equal(),
 		   const allocator_type& __a = allocator_type())
-	: _Hashtable(__f, __l, __bkt_count_hint, __hf, _H2(), _Hash(), __eql,
-		     __key_extract(), __a)
+	: _Hashtable(__f, __l, __bkt_count_hint, __hf, __eql, __key_extract(),
+		     __a)
 	{ }
 
       _Hashtable(initializer_list<value_type> __l,
 		 size_type __bkt_count_hint = 0,
-		 const _H1& __hf = _H1(),
+		 const _Hash& __hf = _Hash(),
 		 const key_equal& __eql = key_equal(),
 		 const allocator_type& __a = allocator_type())
       : _Hashtable(__l.begin(), __l.end(), __bkt_count_hint,
-		   __hf, _H2(), _Hash(), __eql,
-		   __key_extract(), __a)
+		   __hf, __eql, __key_extract(), __a)
       { }
 
       _Hashtable&
@@ -588,7 +559,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _Hashtable&
       operator=(_Hashtable&& __ht)
       noexcept(__node_alloc_traits::_S_nothrow_move()
-	       && is_nothrow_move_assignable<_H1>::value
+	       && is_nothrow_move_assignable<_Hash>::value
 	       && is_nothrow_move_assignable<_Equal>::value)
       {
         constexpr bool __move_storage =
@@ -620,8 +591,8 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       void
       swap(_Hashtable&)
-      noexcept(__and_<__is_nothrow_swappable<_H1>,
-	                  __is_nothrow_swappable<_Equal>>::value);
+      noexcept(__and_<__is_nothrow_swappable<_Hash>,
+		      __is_nothrow_swappable<_Equal>>::value);
 
       // Basic container operations
       iterator
@@ -686,7 +657,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       size_type
       bucket(const key_type& __k) const
-      { return _M_bucket_index(__k, this->_M_hash_code(__k)); }
+      { return _M_bucket_index(this->_M_hash_code(__k)); }
 
       local_iterator
       begin(size_type __bkt)
@@ -764,8 +735,8 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
       { return __hash_code_base::_M_bucket_index(__n, _M_bucket_count); }
 
       size_type
-      _M_bucket_index(const key_type& __k, __hash_code __c) const
-      { return __hash_code_base::_M_bucket_index(__k, __c, _M_bucket_count); }
+      _M_bucket_index(__hash_code __c) const
+      { return __hash_code_base::_M_bucket_index(__c, _M_bucket_count); }
 
       // Find and insert helper functions and types
       // Find the node before the one matching the criteria.
@@ -925,7 +896,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
 	    const key_type& __k = __nh._M_key();
 	    __hash_code __code = this->_M_hash_code(__k);
-	    size_type __bkt = _M_bucket_index(__k, __code);
+	    size_type __bkt = _M_bucket_index(__code);
 	    if (__node_type* __n = _M_find_node(__bkt, __k, __code))
 	      {
 		__ret.node = std::move(__nh);
@@ -1006,7 +977,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
       {
 	node_type __nh;
 	__hash_code __code = this->_M_hash_code(__k);
-	std::size_t __bkt = _M_bucket_index(__k, __code);
+	std::size_t __bkt = _M_bucket_index(__code);
 	if (__node_base* __prev_node = _M_find_before_node(__bkt, __k, __code))
 	  __nh = _M_extract_node(__bkt, __prev_node);
 	return __nh;
@@ -1048,7 +1019,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	      auto __pos = __i++;
 	      const key_type& __k = this->_M_extract()(*__pos);
 	      __hash_code __code = this->_M_hash_code(__k);
-	      size_type __bkt = _M_bucket_index(__k, __code);
+	      size_type __bkt = _M_bucket_index(__code);
 	      if (_M_find_node(__bkt, __k, __code) == nullptr)
 		{
 		  auto __nh = __src.extract(__pos);
@@ -1086,13 +1057,12 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
 
   // Definitions of class template _Hashtable's out-of-line member functions.
-  template<typename _Key, typename _Value,
-	   typename _Alloc, typename _ExtractKey, typename _Equal,
-	   typename _H1, typename _H2, typename _Hash, typename _RehashPolicy,
-	   typename _Traits>
+  template<typename _Key, typename _Value, typename _Alloc,
+	   typename _ExtractKey, typename _Equal,
+	   typename _Hash, typename _RehashPolicy, typename _Traits>
     auto
     _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
-	       _H1, _H2, _Hash, _RehashPolicy, _Traits>::
+	       _Hash, _RehashPolicy, _Traits>::
     _M_bucket_begin(size_type __bkt) const
     -> __node_type*
     {
@@ -1100,17 +1070,15 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
       return __n ? static_cast<__node_type*>(__n->_M_nxt) : nullptr;
     }
 
-  template<typename _Key, typename _Value,
-	   typename _Alloc, typename _ExtractKey, typename _Equal,
-	   typename _H1, typename _H2, typename _Hash, typename _RehashPolicy,
-	   typename _Traits>
+  template<typename _Key, typename _Value, typename _Alloc,
+	   typename _ExtractKey, typename _Equal,
+	   typename _Hash, typename _RehashPolicy, typename _Traits>
     _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
-	       _H1, _H2, _Hash, _RehashPolicy, _Traits>::
+	       _Hash, _RehashPolicy, _Traits>::
     _Hashtable(size_type __bkt_count_hint,
-	       const _H1& __h1, const _H2& __h2, const _Hash& __h,
-	       const _Equal& __eq, const _ExtractKey& __exk,
+	       const _Hash& __h, const _Equal& __eq, const _ExtractKey& __exk,
 	       const allocator_type& __a)
-    : _Hashtable(__h1, __h2, __h, __eq, __exk, __a)
+    : _Hashtable(__h, __eq, __exk, __a)
     {
       auto __bkt_count = _M_rehash_policy._M_next_bkt(__bkt_count_hint);
       if (__bkt_count > _M_bucket_count)
@@ -1120,37 +1088,33 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	}
     }
 
-  template<typename _Key, typename _Value,
-	   typename _Alloc, typename _ExtractKey, typename _Equal,
-	   typename _H1, typename _H2, typename _Hash, typename _RehashPolicy,
-	   typename _Traits>
+  template<typename _Key, typename _Value, typename _Alloc,
+	   typename _ExtractKey, typename _Equal,
+	   typename _Hash, typename _RehashPolicy, typename _Traits>
     template<typename _InputIterator>
       _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
-		 _H1, _H2, _Hash, _RehashPolicy, _Traits>::
+		 _Hash, _RehashPolicy, _Traits>::
       _Hashtable(_InputIterator __f, _InputIterator __l,
 		 size_type __bkt_count_hint,
-		 const _H1& __h1, const _H2& __h2, const _Hash& __h,
-		 const _Equal& __eq, const _ExtractKey& __exk,
+		 const _Hash& __h, const _Equal& __eq, const _ExtractKey& __exk,
 		 const allocator_type& __a, __unique_keys_t)
-      : _Hashtable(__bkt_count_hint, __h1, __h2, __h, __eq, __exk, __a)
+      : _Hashtable(__bkt_count_hint, __h, __eq, __exk, __a)
       {
 	for (; __f != __l; ++__f)
 	  this->insert(*__f);
       }
 
-  template<typename _Key, typename _Value,
-	   typename _Alloc, typename _ExtractKey, typename _Equal,
-	   typename _H1, typename _H2, typename _Hash, typename _RehashPolicy,
-	   typename _Traits>
+  template<typename _Key, typename _Value, typename _Alloc,
+	   typename _ExtractKey, typename _Equal,
+	   typename _Hash, typename _RehashPolicy, typename _Traits>
     template<typename _InputIterator>
       _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
-		 _H1, _H2, _Hash, _RehashPolicy, _Traits>::
+		 _Hash, _RehashPolicy, _Traits>::
       _Hashtable(_InputIterator __f, _InputIterator __l,
 		 size_type __bkt_count_hint,
-		 const _H1& __h1, const _H2& __h2, const _Hash& __h,
-		 const _Equal& __eq, const _ExtractKey& __exk,
+		 const _Hash& __h, const _Equal& __eq, const _ExtractKey& __exk,
 		 const allocator_type& __a, __multi_keys_t)
-      : _Hashtable(__h1, __h2, __h, __eq, __exk, __a)
+      : _Hashtable(__h, __eq, __exk, __a)
       {
 	auto __nb_elems = __detail::__distance_fw(__f, __l);
 	auto __bkt_count =
@@ -1168,13 +1132,12 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  this->insert(*__f);
       }
 
-  template<typename _Key, typename _Value,
-	   typename _Alloc, typename _ExtractKey, typename _Equal,
-	   typename _H1, typename _H2, typename _Hash, typename _RehashPolicy,
-	   typename _Traits>
+  template<typename _Key, typename _Value, typename _Alloc,
+	   typename _ExtractKey, typename _Equal,
+	   typename _Hash, typename _RehashPolicy, typename _Traits>
     auto
     _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
-	       _H1, _H2, _Hash, _RehashPolicy, _Traits>::
+	       _Hash, _RehashPolicy, _Traits>::
     operator=(const _Hashtable& __ht)
     -> _Hashtable&
     {
@@ -1220,14 +1183,13 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
       return *this;
     }
 
-  template<typename _Key, typename _Value,
-	   typename _Alloc, typename _ExtractKey, typename _Equal,
-	   typename _H1, typename _H2, typename _Hash, typename _RehashPolicy,
-	   typename _Traits>
+  template<typename _Key, typename _Value, typename _Alloc,
+	   typename _ExtractKey, typename _Equal,
+	   typename _Hash, typename _RehashPolicy, typename _Traits>
     template<typename _Ht>
       void
       _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
-		 _H1, _H2, _Hash, _RehashPolicy, _Traits>::
+		 _Hash, _RehashPolicy, _Traits>::
       _M_assign_elements(_Ht&& __ht)
       {
 	__bucket_type* __former_buckets = nullptr;
@@ -1271,14 +1233,13 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  }
       }
 
-  template<typename _Key, typename _Value,
-	   typename _Alloc, typename _ExtractKey, typename _Equal,
-	   typename _H1, typename _H2, typename _Hash, typename _RehashPolicy,
-	   typename _Traits>
+  template<typename _Key, typename _Value, typename _Alloc,
+	   typename _ExtractKey, typename _Equal,
+	   typename _Hash, typename _RehashPolicy, typename _Traits>
     template<typename _Ht, typename _NodeGenerator>
       void
       _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
-		 _H1, _H2, _Hash, _RehashPolicy, _Traits>::
+		 _Hash, _RehashPolicy, _Traits>::
       _M_assign(_Ht&& __ht, const _NodeGenerator& __node_gen)
       {
 	__bucket_type* __buckets = nullptr;
@@ -1324,11 +1285,10 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   template<typename _Key, typename _Value,
 	   typename _Alloc, typename _ExtractKey, typename _Equal,
-	   typename _H1, typename _H2, typename _Hash, typename _RehashPolicy,
-	   typename _Traits>
+	   typename _Hash, typename _RehashPolicy, typename _Traits>
     void
     _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
-	       _H1, _H2, _Hash, _RehashPolicy, _Traits>::
+	       _Hash, _RehashPolicy, _Traits>::
     _M_reset() noexcept
     {
       _M_rehash_policy._M_reset();
@@ -1341,11 +1301,10 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   template<typename _Key, typename _Value,
 	   typename _Alloc, typename _ExtractKey, typename _Equal,
-	   typename _H1, typename _H2, typename _Hash, typename _RehashPolicy,
-	   typename _Traits>
+	   typename _Hash, typename _RehashPolicy, typename _Traits>
     void
     _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
-	       _H1, _H2, _Hash, _RehashPolicy, _Traits>::
+	       _Hash, _RehashPolicy, _Traits>::
     _M_move_assign(_Hashtable&& __ht, true_type)
     {
       this->_M_deallocate_nodes(_M_begin());
@@ -1372,11 +1331,10 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   template<typename _Key, typename _Value,
 	   typename _Alloc, typename _ExtractKey, typename _Equal,
-	   typename _H1, typename _H2, typename _Hash, typename _RehashPolicy,
-	   typename _Traits>
+	   typename _Hash, typename _RehashPolicy, typename _Traits>
     void
     _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
-	       _H1, _H2, _Hash, _RehashPolicy, _Traits>::
+	       _Hash, _RehashPolicy, _Traits>::
     _M_move_assign(_Hashtable&& __ht, false_type)
     {
       if (__ht._M_node_allocator() == this->_M_node_allocator())
@@ -1391,10 +1349,9 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   template<typename _Key, typename _Value,
 	   typename _Alloc, typename _ExtractKey, typename _Equal,
-	   typename _H1, typename _H2, typename _Hash, typename _RehashPolicy,
-	   typename _Traits>
+	   typename _Hash, typename _RehashPolicy, typename _Traits>
     _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
-	       _H1, _H2, _Hash, _RehashPolicy, _Traits>::
+	       _Hash, _RehashPolicy, _Traits>::
     _Hashtable(const _Hashtable& __ht)
     : __hashtable_base(__ht),
       __map_base(__ht),
@@ -1412,10 +1369,9 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   template<typename _Key, typename _Value,
 	   typename _Alloc, typename _ExtractKey, typename _Equal,
-	   typename _H1, typename _H2, typename _Hash, typename _RehashPolicy,
-	   typename _Traits>
+	   typename _Hash, typename _RehashPolicy, typename _Traits>
     _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
-	       _H1, _H2, _Hash, _RehashPolicy, _Traits>::
+	       _Hash, _RehashPolicy, _Traits>::
     _Hashtable(const _Hashtable& __ht, const allocator_type& __a)
     : __hashtable_base(__ht),
       __map_base(__ht),
@@ -1432,10 +1388,9 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   template<typename _Key, typename _Value,
 	   typename _Alloc, typename _ExtractKey, typename _Equal,
-	   typename _H1, typename _H2, typename _Hash, typename _RehashPolicy,
-	   typename _Traits>
+	   typename _Hash, typename _RehashPolicy, typename _Traits>
     _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
-	       _H1, _H2, _Hash, _RehashPolicy, _Traits>::
+	       _Hash, _RehashPolicy, _Traits>::
     _Hashtable(_Hashtable&& __ht, __node_alloc_type&& __a, false_type)
     : __hashtable_base(__ht),
       __map_base(__ht),
@@ -1472,10 +1427,9 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   template<typename _Key, typename _Value,
 	   typename _Alloc, typename _ExtractKey, typename _Equal,
-	   typename _H1, typename _H2, typename _Hash, typename _RehashPolicy,
-	   typename _Traits>
+	   typename _Hash, typename _RehashPolicy, typename _Traits>
     _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
-	       _H1, _H2, _Hash, _RehashPolicy, _Traits>::
+	       _Hash, _RehashPolicy, _Traits>::
     ~_Hashtable() noexcept
     {
       clear();
@@ -1484,13 +1438,12 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   template<typename _Key, typename _Value,
 	   typename _Alloc, typename _ExtractKey, typename _Equal,
-	   typename _H1, typename _H2, typename _Hash, typename _RehashPolicy,
-	   typename _Traits>
+	   typename _Hash, typename _RehashPolicy, typename _Traits>
     void
     _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
-	       _H1, _H2, _Hash, _RehashPolicy, _Traits>::
+	       _Hash, _RehashPolicy, _Traits>::
     swap(_Hashtable& __x)
-    noexcept(__and_<__is_nothrow_swappable<_H1>,
+    noexcept(__and_<__is_nothrow_swappable<_Hash>,
 	                __is_nothrow_swappable<_Equal>>::value)
     {
       // The only base class with member variables is hash_code_base.
@@ -1530,41 +1483,38 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   template<typename _Key, typename _Value,
 	   typename _Alloc, typename _ExtractKey, typename _Equal,
-	   typename _H1, typename _H2, typename _Hash, typename _RehashPolicy,
-	   typename _Traits>
+	   typename _Hash, typename _RehashPolicy, typename _Traits>
     auto
     _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
-	       _H1, _H2, _Hash, _RehashPolicy, _Traits>::
+	       _Hash, _RehashPolicy, _Traits>::
     find(const key_type& __k)
     -> iterator
     {
       __hash_code __code = this->_M_hash_code(__k);
-      std::size_t __bkt = _M_bucket_index(__k, __code);
+      std::size_t __bkt = _M_bucket_index(__code);
       return iterator(_M_find_node(__bkt, __k, __code));
     }
 
   template<typename _Key, typename _Value,
 	   typename _Alloc, typename _ExtractKey, typename _Equal,
-	   typename _H1, typename _H2, typename _Hash, typename _RehashPolicy,
-	   typename _Traits>
+	   typename _Hash, typename _RehashPolicy, typename _Traits>
     auto
     _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
-	       _H1, _H2, _Hash, _RehashPolicy, _Traits>::
+	       _Hash, _RehashPolicy, _Traits>::
     find(const key_type& __k) const
     -> const_iterator
     {
       __hash_code __code = this->_M_hash_code(__k);
-      std::size_t __bkt = _M_bucket_index(__k, __code);
+      std::size_t __bkt = _M_bucket_index(__code);
       return const_iterator(_M_find_node(__bkt, __k, __code));
     }
 
   template<typename _Key, typename _Value,
 	   typename _Alloc, typename _ExtractKey, typename _Equal,
-	   typename _H1, typename _H2, typename _Hash, typename _RehashPolicy,
-	   typename _Traits>
+	   typename _Hash, typename _RehashPolicy, typename _Traits>
     auto
     _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
-	       _H1, _H2, _Hash, _RehashPolicy, _Traits>::
+	       _Hash, _RehashPolicy, _Traits>::
     count(const key_type& __k) const
     -> size_type
     {
@@ -1589,11 +1539,10 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   template<typename _Key, typename _Value,
 	   typename _Alloc, typename _ExtractKey, typename _Equal,
-	   typename _H1, typename _H2, typename _Hash, typename _RehashPolicy,
-	   typename _Traits>
+	   typename _Hash, typename _RehashPolicy, typename _Traits>
     auto
     _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
-	       _H1, _H2, _Hash, _RehashPolicy, _Traits>::
+	       _Hash, _RehashPolicy, _Traits>::
     equal_range(const key_type& __k)
     -> pair<iterator, iterator>
     {
@@ -1616,11 +1565,10 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   template<typename _Key, typename _Value,
 	   typename _Alloc, typename _ExtractKey, typename _Equal,
-	   typename _H1, typename _H2, typename _Hash, typename _RehashPolicy,
-	   typename _Traits>
+	   typename _Hash, typename _RehashPolicy, typename _Traits>
     auto
     _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
-	       _H1, _H2, _Hash, _RehashPolicy, _Traits>::
+	       _Hash, _RehashPolicy, _Traits>::
     equal_range(const key_type& __k) const
     -> pair<const_iterator, const_iterator>
     {
@@ -1645,11 +1593,10 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
   // Return nullptr if no node is found.
   template<typename _Key, typename _Value,
 	   typename _Alloc, typename _ExtractKey, typename _Equal,
-	   typename _H1, typename _H2, typename _Hash, typename _RehashPolicy,
-	   typename _Traits>
+	   typename _Hash, typename _RehashPolicy, typename _Traits>
     auto
     _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
-	       _H1, _H2, _Hash, _RehashPolicy, _Traits>::
+	       _Hash, _RehashPolicy, _Traits>::
     _M_find_before_node(size_type __bkt, const key_type& __k,
 			__hash_code __code) const
     -> __node_base*
@@ -1674,11 +1621,10 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   template<typename _Key, typename _Value,
 	   typename _Alloc, typename _ExtractKey, typename _Equal,
-	   typename _H1, typename _H2, typename _Hash, typename _RehashPolicy,
-	   typename _Traits>
+	   typename _Hash, typename _RehashPolicy, typename _Traits>
     void
     _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
-	       _H1, _H2, _Hash, _RehashPolicy, _Traits>::
+	       _Hash, _RehashPolicy, _Traits>::
     _M_insert_bucket_begin(size_type __bkt, __node_type* __node)
     {
       if (_M_buckets[__bkt])
@@ -1707,11 +1653,10 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   template<typename _Key, typename _Value,
 	   typename _Alloc, typename _ExtractKey, typename _Equal,
-	   typename _H1, typename _H2, typename _Hash, typename _RehashPolicy,
-	   typename _Traits>
+	   typename _Hash, typename _RehashPolicy, typename _Traits>
     void
     _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
-	       _H1, _H2, _Hash, _RehashPolicy, _Traits>::
+	       _Hash, _RehashPolicy, _Traits>::
     _M_remove_bucket_begin(size_type __bkt, __node_type* __next,
 			   size_type __next_bkt)
     {
@@ -1731,11 +1676,10 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   template<typename _Key, typename _Value,
 	   typename _Alloc, typename _ExtractKey, typename _Equal,
-	   typename _H1, typename _H2, typename _Hash, typename _RehashPolicy,
-	   typename _Traits>
+	   typename _Hash, typename _RehashPolicy, typename _Traits>
     auto
     _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
-	       _H1, _H2, _Hash, _RehashPolicy, _Traits>::
+	       _Hash, _RehashPolicy, _Traits>::
     _M_get_previous_node(size_type __bkt, __node_base* __n)
     -> __node_base*
     {
@@ -1747,12 +1691,11 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   template<typename _Key, typename _Value,
 	   typename _Alloc, typename _ExtractKey, typename _Equal,
-	   typename _H1, typename _H2, typename _Hash, typename _RehashPolicy,
-	   typename _Traits>
+	   typename _Hash, typename _RehashPolicy, typename _Traits>
     template<typename... _Args>
       auto
       _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
-		 _H1, _H2, _Hash, _RehashPolicy, _Traits>::
+		 _Hash, _RehashPolicy, _Traits>::
       _M_emplace(__unique_keys_t __uks, _Args&&... __args)
       -> pair<iterator, bool>
       {
@@ -1760,7 +1703,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	_Scoped_node __node { this, std::forward<_Args>(__args)...  };
 	const key_type& __k = this->_M_extract()(__node._M_node->_M_v());
 	__hash_code __code = this->_M_hash_code(__k);
-	size_type __bkt = _M_bucket_index(__k, __code);
+	size_type __bkt = _M_bucket_index(__code);
 	if (__node_type* __p = _M_find_node(__bkt, __k, __code))
 	  // There is already an equivalent node, no insertion
 	  return std::make_pair(iterator(__p), false);
@@ -1773,12 +1716,11 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   template<typename _Key, typename _Value,
 	   typename _Alloc, typename _ExtractKey, typename _Equal,
-	   typename _H1, typename _H2, typename _Hash, typename _RehashPolicy,
-	   typename _Traits>
+	   typename _Hash, typename _RehashPolicy, typename _Traits>
     template<typename... _Args>
       auto
       _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
-		 _H1, _H2, _Hash, _RehashPolicy, _Traits>::
+		 _Hash, _RehashPolicy, _Traits>::
       _M_emplace(const_iterator __hint, __multi_keys_t __mks, _Args&&... __args)
       -> iterator
       {
@@ -1795,11 +1737,10 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   template<typename _Key, typename _Value,
 	   typename _Alloc, typename _ExtractKey, typename _Equal,
-	   typename _H1, typename _H2, typename _Hash, typename _RehashPolicy,
-	   typename _Traits>
+	   typename _Hash, typename _RehashPolicy, typename _Traits>
     auto
     _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
-	       _H1, _H2, _Hash, _RehashPolicy, _Traits>::
+	       _Hash, _RehashPolicy, _Traits>::
     _M_insert_node(__unique_keys_t, size_type __bkt, __hash_code __code,
 		   __node_type* __node, size_type __n_elt)
     -> iterator
@@ -1812,7 +1753,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
       if (__do_rehash.first)
 	{
 	  _M_rehash(__do_rehash.second, __saved_state);
-	  __bkt = _M_bucket_index(this->_M_extract()(__node->_M_v()), __code);
+	  __bkt = _M_bucket_index(__code);
 	}
 
       this->_M_store_code(__node, __code);
@@ -1825,11 +1766,10 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   template<typename _Key, typename _Value,
 	   typename _Alloc, typename _ExtractKey, typename _Equal,
-	   typename _H1, typename _H2, typename _Hash, typename _RehashPolicy,
-	   typename _Traits>
+	   typename _Hash, typename _RehashPolicy, typename _Traits>
     auto
     _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
-	       _H1, _H2, _Hash, _RehashPolicy, _Traits>::
+	       _Hash, _RehashPolicy, _Traits>::
     _M_insert_node(__multi_keys_t, __node_type* __hint,
 		   __hash_code __code, __node_type* __node)
     -> iterator
@@ -1843,7 +1783,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       this->_M_store_code(__node, __code);
       const key_type& __k = this->_M_extract()(__node->_M_v());
-      size_type __bkt = _M_bucket_index(__k, __code);
+      size_type __bkt = _M_bucket_index(__code);
 
       // Find the node before an equivalent one or use hint if it exists and
       // if it is equivalent.
@@ -1880,19 +1820,18 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
   // Insert v if no element with its key is already present.
   template<typename _Key, typename _Value,
 	   typename _Alloc, typename _ExtractKey, typename _Equal,
-	   typename _H1, typename _H2, typename _Hash, typename _RehashPolicy,
-	   typename _Traits>
+	   typename _Hash, typename _RehashPolicy, typename _Traits>
     template<typename _Arg, typename _NodeGenerator>
       auto
       _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
-		 _H1, _H2, _Hash, _RehashPolicy, _Traits>::
+		 _Hash, _RehashPolicy, _Traits>::
       _M_insert(_Arg&& __v, const _NodeGenerator& __node_gen,
 		__unique_keys_t __uks)
       -> pair<iterator, bool>
       {
 	const key_type& __k = this->_M_extract()(__v);
 	__hash_code __code = this->_M_hash_code(__k);
-	size_type __bkt = _M_bucket_index(__k, __code);
+	size_type __bkt = _M_bucket_index(__code);
 
 	if (__node_type* __node = _M_find_node(__bkt, __k, __code))
 	  return { iterator(__node), false };
@@ -1906,12 +1845,11 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
   // Insert v unconditionally.
   template<typename _Key, typename _Value,
 	   typename _Alloc, typename _ExtractKey, typename _Equal,
-	   typename _H1, typename _H2, typename _Hash, typename _RehashPolicy,
-	   typename _Traits>
+	   typename _Hash, typename _RehashPolicy, typename _Traits>
     template<typename _Arg, typename _NodeGenerator>
       auto
       _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
-		 _H1, _H2, _Hash, _RehashPolicy, _Traits>::
+		 _Hash, _RehashPolicy, _Traits>::
       _M_insert(const_iterator __hint, _Arg&& __v,
 		const _NodeGenerator& __node_gen, __multi_keys_t __mks)
       -> iterator
@@ -1931,11 +1869,10 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   template<typename _Key, typename _Value,
 	   typename _Alloc, typename _ExtractKey, typename _Equal,
-	   typename _H1, typename _H2, typename _Hash, typename _RehashPolicy,
-	   typename _Traits>
+	   typename _Hash, typename _RehashPolicy, typename _Traits>
     auto
     _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
-	       _H1, _H2, _Hash, _RehashPolicy, _Traits>::
+	       _Hash, _RehashPolicy, _Traits>::
     erase(const_iterator __it)
     -> iterator
     {
@@ -1951,11 +1888,10 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   template<typename _Key, typename _Value,
 	   typename _Alloc, typename _ExtractKey, typename _Equal,
-	   typename _H1, typename _H2, typename _Hash, typename _RehashPolicy,
-	   typename _Traits>
+	   typename _Hash, typename _RehashPolicy, typename _Traits>
     auto
     _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
-	       _H1, _H2, _Hash, _RehashPolicy, _Traits>::
+	       _Hash, _RehashPolicy, _Traits>::
     _M_erase(size_type __bkt, __node_base* __prev_n, __node_type* __n)
     -> iterator
     {
@@ -1979,16 +1915,15 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   template<typename _Key, typename _Value,
 	   typename _Alloc, typename _ExtractKey, typename _Equal,
-	   typename _H1, typename _H2, typename _Hash, typename _RehashPolicy,
-	   typename _Traits>
+	   typename _Hash, typename _RehashPolicy, typename _Traits>
     auto
     _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
-	       _H1, _H2, _Hash, _RehashPolicy, _Traits>::
+	       _Hash, _RehashPolicy, _Traits>::
     _M_erase(__unique_keys_t, const key_type& __k)
     -> size_type
     {
       __hash_code __code = this->_M_hash_code(__k);
-      std::size_t __bkt = _M_bucket_index(__k, __code);
+      std::size_t __bkt = _M_bucket_index(__code);
 
       // Look for the node before the first matching node.
       __node_base* __prev_n = _M_find_before_node(__bkt, __k, __code);
@@ -2003,16 +1938,15 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   template<typename _Key, typename _Value,
 	   typename _Alloc, typename _ExtractKey, typename _Equal,
-	   typename _H1, typename _H2, typename _Hash, typename _RehashPolicy,
-	   typename _Traits>
+	   typename _Hash, typename _RehashPolicy, typename _Traits>
     auto
     _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
-	       _H1, _H2, _Hash, _RehashPolicy, _Traits>::
+	       _Hash, _RehashPolicy, _Traits>::
     _M_erase(__multi_keys_t, const key_type& __k)
     -> size_type
     {
       __hash_code __code = this->_M_hash_code(__k);
-      std::size_t __bkt = _M_bucket_index(__k, __code);
+      std::size_t __bkt = _M_bucket_index(__code);
 
       // Look for the node before the first matching node.
       __node_base* __prev_n = _M_find_before_node(__bkt, __k, __code);
@@ -2054,11 +1988,10 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   template<typename _Key, typename _Value,
 	   typename _Alloc, typename _ExtractKey, typename _Equal,
-	   typename _H1, typename _H2, typename _Hash, typename _RehashPolicy,
-	   typename _Traits>
+	   typename _Hash, typename _RehashPolicy, typename _Traits>
     auto
     _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
-	       _H1, _H2, _Hash, _RehashPolicy, _Traits>::
+	       _Hash, _RehashPolicy, _Traits>::
     erase(const_iterator __first, const_iterator __last)
     -> iterator
     {
@@ -2101,11 +2034,10 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   template<typename _Key, typename _Value,
 	   typename _Alloc, typename _ExtractKey, typename _Equal,
-	   typename _H1, typename _H2, typename _Hash, typename _RehashPolicy,
-	   typename _Traits>
+	   typename _Hash, typename _RehashPolicy, typename _Traits>
     void
     _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
-	       _H1, _H2, _Hash, _RehashPolicy, _Traits>::
+	       _Hash, _RehashPolicy, _Traits>::
     clear() noexcept
     {
       this->_M_deallocate_nodes(_M_begin());
@@ -2116,11 +2048,10 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   template<typename _Key, typename _Value,
 	   typename _Alloc, typename _ExtractKey, typename _Equal,
-	   typename _H1, typename _H2, typename _Hash, typename _RehashPolicy,
-	   typename _Traits>
+	   typename _Hash, typename _RehashPolicy, typename _Traits>
     void
     _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
-	       _H1, _H2, _Hash, _RehashPolicy, _Traits>::
+	       _Hash, _RehashPolicy, _Traits>::
     rehash(size_type __bkt_count)
     {
       const __rehash_state& __saved_state = _M_rehash_policy._M_state();
@@ -2139,11 +2070,10 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   template<typename _Key, typename _Value,
 	   typename _Alloc, typename _ExtractKey, typename _Equal,
-	   typename _H1, typename _H2, typename _Hash, typename _RehashPolicy,
-	   typename _Traits>
+	   typename _Hash, typename _RehashPolicy, typename _Traits>
     void
     _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
-	       _H1, _H2, _Hash, _RehashPolicy, _Traits>::
+	       _Hash, _RehashPolicy, _Traits>::
     _M_rehash(size_type __bkt_count, const __rehash_state& __state)
     {
       __try
@@ -2162,11 +2092,10 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
   // Rehash when there is no equivalent elements.
   template<typename _Key, typename _Value,
 	   typename _Alloc, typename _ExtractKey, typename _Equal,
-	   typename _H1, typename _H2, typename _Hash, typename _RehashPolicy,
-	   typename _Traits>
+	   typename _Hash, typename _RehashPolicy, typename _Traits>
     void
     _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
-	       _H1, _H2, _Hash, _RehashPolicy, _Traits>::
+	       _Hash, _RehashPolicy, _Traits>::
     _M_rehash_aux(size_type __bkt_count, __unique_keys_t)
     {
       __bucket_type* __new_buckets = _M_allocate_buckets(__bkt_count);
@@ -2205,11 +2134,10 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
   // order.
   template<typename _Key, typename _Value,
 	   typename _Alloc, typename _ExtractKey, typename _Equal,
-	   typename _H1, typename _H2, typename _Hash, typename _RehashPolicy,
-	   typename _Traits>
+	   typename _Hash, typename _RehashPolicy, typename _Traits>
     void
     _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
-	       _H1, _H2, _Hash, _RehashPolicy, _Traits>::
+	       _Hash, _RehashPolicy, _Traits>::
     _M_rehash_aux(size_type __bkt_count, __multi_keys_t)
     {
       __bucket_type* __new_buckets = _M_allocate_buckets(__bkt_count);
diff --git a/libstdc++-v3/include/bits/hashtable_policy.h b/libstdc++-v3/include/bits/hashtable_policy.h
index 5cc943b3d22..11ea47b322e 100644
--- a/libstdc++-v3/include/bits/hashtable_policy.h
+++ b/libstdc++-v3/include/bits/hashtable_policy.h
@@ -41,8 +41,8 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   template<typename _Key, typename _Value, typename _Alloc,
 	   typename _ExtractKey, typename _Equal,
-	   typename _H1, typename _H2, typename _Hash,
-	   typename _RehashPolicy, typename _Traits>
+	   typename _Hash, typename _RehashPolicy,
+	   typename _Traits>
     class _Hashtable;
 
 namespace __detail
@@ -54,7 +54,8 @@  namespace __detail
    */
   template<typename _Key, typename _Value,
 	   typename _ExtractKey, typename _Equal,
-	   typename _H1, typename _H2, typename _Hash, typename _Traits>
+	   typename _Hash, typename _RangedHash,
+	   typename _Traits>
     struct _Hashtable_base;
 
   // Helper function: return distance(first, last) for forward
@@ -442,18 +443,12 @@  namespace __detail
     { return __num % __den; }
   };
 
-  /// Default ranged hash function H.  In principle it should be a
-  /// function object composed from objects of type H1 and H2 such that
-  /// h(k, N) = h2(h1(k), N), but that would mean making extra copies of
-  /// h1 and h2.  So instead we'll just use a tag to tell class template
-  /// hashtable to do that composition.
-  struct _Default_ranged_hash { };
-
   /// Default value for rehash policy.  Bucket size is (usually) the
   /// smallest prime that keeps the load factor small enough.
   struct _Prime_rehash_policy
   {
     using __has_load_factor = true_type;
+    using __ranged_hash = _Mod_range_hashing;
 
     _Prime_rehash_policy(float __z = 1.0) noexcept
     : _M_max_load_factor(__z), _M_next_resize(0) { }
@@ -531,6 +526,7 @@  namespace __detail
   struct _Power2_rehash_policy
   {
     using __has_load_factor = true_type;
+    using __ranged_hash = _Mask_range_hashing;
 
     _Power2_rehash_policy(float __z = 1.0) noexcept
     : _M_max_load_factor(__z), _M_next_resize(0) { }
@@ -648,37 +644,33 @@  namespace __detail
    */
   template<typename _Key, typename _Value, typename _Alloc,
 	   typename _ExtractKey, typename _Equal,
-	   typename _H1, typename _H2, typename _Hash,
-	   typename _RehashPolicy, typename _Traits,
+	   typename _Hash, typename _RehashPolicy, typename _Traits,
 	   typename = typename _Traits::__unique_keys>
     struct _Map_base { };
 
   /// Partial specialization, keys are not unique.
   template<typename _Key, typename _Pair, typename _Alloc, typename _Equal,
-	   typename _H1, typename _H2, typename _Hash,
-	   typename _RehashPolicy, typename _Traits>
+	   typename _Hash, typename _RehashPolicy, typename _Traits>
     struct _Map_base<_Key, _Pair, _Alloc, _Select1st, _Equal,
-		     _H1, _H2, _Hash, _RehashPolicy, _Traits, __multi_keys_t>
+		     _Hash, _RehashPolicy, _Traits, __multi_keys_t>
     {
       using mapped_type = typename std::tuple_element<1, _Pair>::type;
     };
 
   /// Partial specialization, keys are unique.
   template<typename _Key, typename _Pair, typename _Alloc, typename _Equal,
-	   typename _H1, typename _H2, typename _Hash,
-	   typename _RehashPolicy, typename _Traits>
+	   typename _Hash, typename _RehashPolicy, typename _Traits>
     struct _Map_base<_Key, _Pair, _Alloc, _Select1st, _Equal,
-		     _H1, _H2, _Hash, _RehashPolicy, _Traits, __unique_keys_t>
+		     _Hash, _RehashPolicy, _Traits, __unique_keys_t>
     {
     private:
-      using __hashtable_base = __detail::_Hashtable_base<_Key, _Pair,
-							 _Select1st,
-							_Equal, _H1, _H2, _Hash,
-							  _Traits>;
+      using __hashtable_base =
+	__detail::_Hashtable_base<_Key, _Pair, _Select1st, _Equal,
+				  _Hash, typename _RehashPolicy::__ranged_hash,
+				  _Traits>;
 
-      using __hashtable = _Hashtable<_Key, _Pair, _Alloc,
-				     _Select1st, _Equal,
-				     _H1, _H2, _Hash, _RehashPolicy, _Traits>;
+      using __hashtable = _Hashtable<_Key, _Pair, _Alloc, _Select1st, _Equal,
+				     _Hash, _RehashPolicy, _Traits>;
 
       using __hash_code = typename __hashtable_base::__hash_code;
       using __node_type = typename __hashtable_base::__node_type;
@@ -704,17 +696,16 @@  namespace __detail
     };
 
   template<typename _Key, typename _Pair, typename _Alloc, typename _Equal,
-	   typename _H1, typename _H2, typename _Hash,
-	   typename _RehashPolicy, typename _Traits>
+	   typename _Hash, typename _RehashPolicy, typename _Traits>
     auto
     _Map_base<_Key, _Pair, _Alloc, _Select1st, _Equal,
-	      _H1, _H2, _Hash, _RehashPolicy, _Traits, __unique_keys_t>::
+	      _Hash, _RehashPolicy, _Traits, __unique_keys_t>::
     operator[](const key_type& __k)
     -> mapped_type&
     {
       __hashtable* __h = static_cast<__hashtable*>(this);
       __hash_code __code = __h->_M_hash_code(__k);
-      std::size_t __bkt = __h->_M_bucket_index(__k, __code);
+      std::size_t __bkt = __h->_M_bucket_index(__code);
       if (__node_type* __node = __h->_M_find_node(__bkt, __k, __code))
 	return __node->_M_v().second;
 
@@ -731,17 +722,16 @@  namespace __detail
     }
 
   template<typename _Key, typename _Pair, typename _Alloc, typename _Equal,
-	   typename _H1, typename _H2, typename _Hash,
-	   typename _RehashPolicy, typename _Traits>
+	   typename _Hash, typename _RehashPolicy, typename _Traits>
     auto
     _Map_base<_Key, _Pair, _Alloc, _Select1st, _Equal,
-	      _H1, _H2, _Hash, _RehashPolicy, _Traits, __unique_keys_t>::
+	      _Hash, _RehashPolicy, _Traits, __unique_keys_t>::
     operator[](key_type&& __k)
     -> mapped_type&
     {
       __hashtable* __h = static_cast<__hashtable*>(this);
       __hash_code __code = __h->_M_hash_code(__k);
-      std::size_t __bkt = __h->_M_bucket_index(__k, __code);
+      std::size_t __bkt = __h->_M_bucket_index(__code);
       if (__node_type* __node = __h->_M_find_node(__bkt, __k, __code))
 	return __node->_M_v().second;
 
@@ -758,11 +748,10 @@  namespace __detail
     }
 
   template<typename _Key, typename _Pair, typename _Alloc, typename _Equal,
-	   typename _H1, typename _H2, typename _Hash,
-	   typename _RehashPolicy, typename _Traits>
+	   typename _Hash, typename _RehashPolicy, typename _Traits>
     auto
     _Map_base<_Key, _Pair, _Alloc, _Select1st, _Equal,
-	      _H1, _H2, _Hash, _RehashPolicy, _Traits, __unique_keys_t>::
+	      _Hash, _RehashPolicy, _Traits, __unique_keys_t>::
     at(const key_type& __k)
     -> mapped_type&
     {
@@ -775,11 +764,10 @@  namespace __detail
     }
 
   template<typename _Key, typename _Pair, typename _Alloc, typename _Equal,
-	   typename _H1, typename _H2, typename _Hash,
-	   typename _RehashPolicy, typename _Traits>
+	   typename _Hash, typename _RehashPolicy, typename _Traits>
     auto
     _Map_base<_Key, _Pair, _Alloc, _Select1st, _Equal,
-	      _H1, _H2, _Hash, _RehashPolicy, _Traits, __unique_keys_t>::
+	      _Hash, _RehashPolicy, _Traits, __unique_keys_t>::
     at(const key_type& __k) const
     -> const mapped_type&
     {
@@ -798,18 +786,16 @@  namespace __detail
    */
   template<typename _Key, typename _Value, typename _Alloc,
 	   typename _ExtractKey, typename _Equal,
-	   typename _H1, typename _H2, typename _Hash,
-	   typename _RehashPolicy, typename _Traits>
+	   typename _Hash, typename _RehashPolicy, typename _Traits>
     struct _Insert_base
     {
     protected:
       using __hashtable = _Hashtable<_Key, _Value, _Alloc, _ExtractKey,
-				     _Equal, _H1, _H2, _Hash,
-				     _RehashPolicy, _Traits>;
+				     _Equal, _Hash, _RehashPolicy, _Traits>;
 
-      using __hashtable_base = _Hashtable_base<_Key, _Value, _ExtractKey,
-					       _Equal, _H1, _H2, _Hash,
-					       _Traits>;
+      using __hashtable_base =
+	_Hashtable_base<_Key, _Value, _ExtractKey, _Equal,
+			_Hash, typename _RehashPolicy::__ranged_hash, _Traits>;
 
       using value_type = typename __hashtable_base::value_type;
       using iterator = typename __hashtable_base::iterator;
@@ -871,12 +857,11 @@  namespace __detail
 
   template<typename _Key, typename _Value, typename _Alloc,
 	   typename _ExtractKey, typename _Equal,
-	   typename _H1, typename _H2, typename _Hash,
-	   typename _RehashPolicy, typename _Traits>
+	   typename _Hash, typename _RehashPolicy, typename _Traits>
     template<typename _InputIterator, typename _NodeGetter>
       void
-      _Insert_base<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash,
-		    _RehashPolicy, _Traits>::
+      _Insert_base<_Key, _Value, _Alloc, _ExtractKey, _Equal,
+		   _Hash, _RehashPolicy, _Traits>::
       _M_insert_range(_InputIterator __first, _InputIterator __last,
 		      const _NodeGetter& __node_gen, __unique_keys_t __uks)
       {
@@ -887,12 +872,11 @@  namespace __detail
 
   template<typename _Key, typename _Value, typename _Alloc,
 	   typename _ExtractKey, typename _Equal,
-	   typename _H1, typename _H2, typename _Hash,
-	   typename _RehashPolicy, typename _Traits>
+	   typename _Hash, typename _RehashPolicy, typename _Traits>
     template<typename _InputIterator, typename _NodeGetter>
       void
-      _Insert_base<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash,
-		    _RehashPolicy, _Traits>::
+      _Insert_base<_Key, _Value, _Alloc, _ExtractKey, _Equal,
+		   _Hash, _RehashPolicy, _Traits>::
       _M_insert_range(_InputIterator __first, _InputIterator __last,
 		      const _NodeGetter& __node_gen, __multi_keys_t __mks)
       {
@@ -926,28 +910,25 @@  namespace __detail
    */
   template<typename _Key, typename _Value, typename _Alloc,
 	   typename _ExtractKey, typename _Equal,
-	   typename _H1, typename _H2, typename _Hash,
-	   typename _RehashPolicy, typename _Traits,
+	   typename _Hash, typename _RehashPolicy, typename _Traits,
 	   typename = typename _Traits::__constant_iterators>
     struct _Insert;
 
   /// Specialization.
   template<typename _Key, typename _Value, typename _Alloc,
 	   typename _ExtractKey, typename _Equal,
-	   typename _H1, typename _H2, typename _Hash,
-	   typename _RehashPolicy, typename _Traits>
-    struct _Insert<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash,
+	   typename _Hash, typename _RehashPolicy, typename _Traits>
+    struct _Insert<_Key, _Value, _Alloc, _ExtractKey, _Equal, _Hash,
 		   _RehashPolicy, _Traits, __constant_iterators_t>
     : public _Insert_base<_Key, _Value, _Alloc, _ExtractKey, _Equal,
-			   _H1, _H2, _Hash, _RehashPolicy, _Traits>
+			   _Hash, _RehashPolicy, _Traits>
     {
       using __base_type = _Insert_base<_Key, _Value, _Alloc, _ExtractKey,
-					_Equal, _H1, _H2, _Hash,
-					_RehashPolicy, _Traits>;
+					_Equal, _Hash, _RehashPolicy, _Traits>;
 
-      using __hashtable_base = _Hashtable_base<_Key, _Value, _ExtractKey,
-					       _Equal, _H1, _H2, _Hash,
-					       _Traits>;
+      using __hashtable_base =
+	_Hashtable_base<_Key, _Value, _ExtractKey, _Equal, _Hash,
+			typename _RehashPolicy::__ranged_hash, _Traits>;
 
       using value_type = typename __base_type::value_type;
       using iterator = typename __base_type::iterator;
@@ -981,16 +962,14 @@  namespace __detail
   /// Specialization.
   template<typename _Key, typename _Value, typename _Alloc,
 	   typename _ExtractKey, typename _Equal,
-	   typename _H1, typename _H2, typename _Hash,
-	   typename _RehashPolicy, typename _Traits>
-    struct _Insert<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash,
-		   _RehashPolicy, _Traits, __mutable_iterators_t>
+	   typename _Hash, typename _RehashPolicy, typename _Traits>
+    struct _Insert<_Key, _Value, _Alloc, _ExtractKey, _Equal,
+		   _Hash, _RehashPolicy, _Traits, __mutable_iterators_t>
     : public _Insert_base<_Key, _Value, _Alloc, _ExtractKey, _Equal,
-			   _H1, _H2, _Hash, _RehashPolicy, _Traits>
+			  _Hash, _RehashPolicy, _Traits>
     {
       using __base_type = _Insert_base<_Key, _Value, _Alloc, _ExtractKey,
-				       _Equal, _H1, _H2, _Hash,
-				       _RehashPolicy, _Traits>;
+				       _Equal, _Hash, _RehashPolicy, _Traits>;
       using value_type = typename __base_type::value_type;
       using iterator = typename __base_type::iterator;
       using const_iterator =  typename __base_type::const_iterator;
@@ -1039,8 +1018,7 @@  namespace __detail
   */
   template<typename _Key, typename _Value, typename _Alloc,
 	   typename _ExtractKey, typename _Equal,
-	   typename _H1, typename _H2, typename _Hash,
-	   typename _RehashPolicy, typename _Traits,
+	   typename _Hash, typename _RehashPolicy, typename _Traits,
 	   typename =
 	     __detected_or_t<false_type, __has_load_factor, _RehashPolicy>>
     struct _Rehash_base;
@@ -1048,26 +1026,21 @@  namespace __detail
   /// Specialization when rehash policy doesn't provide load factor management.
   template<typename _Key, typename _Value, typename _Alloc,
 	   typename _ExtractKey, typename _Equal,
-	   typename _H1, typename _H2, typename _Hash,
-	   typename _RehashPolicy, typename _Traits>
+	   typename _Hash, typename _RehashPolicy, typename _Traits>
     struct _Rehash_base<_Key, _Value, _Alloc, _ExtractKey, _Equal,
-		      _H1, _H2, _Hash, _RehashPolicy, _Traits,
-		      false_type>
+		      _Hash, _RehashPolicy, _Traits, false_type>
     {
     };
 
   /// Specialization when rehash policy provide load factor management.
   template<typename _Key, typename _Value, typename _Alloc,
 	   typename _ExtractKey, typename _Equal,
-	   typename _H1, typename _H2, typename _Hash,
-	   typename _RehashPolicy, typename _Traits>
+	   typename _Hash, typename _RehashPolicy, typename _Traits>
     struct _Rehash_base<_Key, _Value, _Alloc, _ExtractKey, _Equal,
-			_H1, _H2, _Hash, _RehashPolicy, _Traits,
-			true_type>
+			_Hash, _RehashPolicy, _Traits, true_type>
     {
       using __hashtable = _Hashtable<_Key, _Value, _Alloc, _ExtractKey,
-				     _Equal, _H1, _H2, _Hash,
-				     _RehashPolicy, _Traits>;
+				     _Equal, _Hash, _RehashPolicy, _Traits>;
 
       float
       max_load_factor() const noexcept
@@ -1142,7 +1115,7 @@  namespace __detail
    *  but not between buckets.
    */
   template<typename _Key, typename _Value, typename _ExtractKey,
-	   typename _H1, typename _H2, typename _Hash,
+	   typename _Hash, typename _RangedHash,
 	   typename _Cache_hash_code>
     struct _Local_iterator_base;
 
@@ -1167,111 +1140,35 @@  namespace __detail
    *  Primary template is unused except as a hook for specializations.
    */
   template<typename _Key, typename _Value, typename _ExtractKey,
-	   typename _H1, typename _H2, typename _Hash,
+	   typename _Hash, typename _RangedHash,
 	   typename _Cache_hash_code>
     struct _Hash_code_base;
 
-  /// Specialization: ranged hash function, no caching hash codes.  H1
-  /// and H2 are provided but ignored.  We define a dummy hash code type.
-  template<typename _Key, typename _Value, typename _ExtractKey,
-	   typename _H1, typename _H2, typename _Hash>
-    struct _Hash_code_base<_Key, _Value, _ExtractKey, _H1, _H2, _Hash,
-			   __hash_not_cached_t>
-    : private _Hashtable_ebo_helper<0, _ExtractKey>,
-      private _Hashtable_ebo_helper<1, _Hash>
-    {
-    private:
-      using __ebo_extract_key = _Hashtable_ebo_helper<0, _ExtractKey>;
-      using __ebo_hash = _Hashtable_ebo_helper<1, _Hash>;
-
-    protected:
-      typedef void* 					__hash_code;
-      typedef _Hash_node<_Value, __hash_not_cached_t>	__node_type;
-
-      // We need the default constructor for the local iterators and _Hashtable
-      // default constructor.
-      _Hash_code_base() = default;
-
-      _Hash_code_base(const _ExtractKey& __ex, const _H1&, const _H2&,
-		      const _Hash& __h)
-      : __ebo_extract_key(__ex), __ebo_hash(__h) { }
-
-      __hash_code
-      _M_hash_code(const _Key& __key) const
-      { return 0; }
-
-      std::size_t
-      _M_bucket_index(const _Key& __k, __hash_code,
-		      std::size_t __bkt_count) const
-      { return _M_ranged_hash()(__k, __bkt_count); }
-
-      std::size_t
-      _M_bucket_index(const __node_type* __p, std::size_t __bkt_count) const
-	noexcept( noexcept(declval<const _Hash&>()(declval<const _Key&>(),
-						   (std::size_t)0)) )
-      { return _M_ranged_hash()(_M_extract()(__p->_M_v()), __bkt_count); }
-
-      void
-      _M_store_code(__node_type*, __hash_code) const
-      { }
-
-      void
-      _M_copy_code(__node_type*, const __node_type*) const
-      { }
-
-      void
-      _M_swap(_Hash_code_base& __x)
-      {
-	std::swap(__ebo_extract_key::_M_get(),
-		  __x.__ebo_extract_key::_M_get());
-	std::swap(__ebo_hash::_M_get(), __x.__ebo_hash::_M_get());
-      }
-
-      const _ExtractKey&
-      _M_extract() const { return __ebo_extract_key::_M_cget(); }
-
-      const _Hash&
-      _M_ranged_hash() const { return __ebo_hash::_M_cget(); }
-    };
-
-  // No specialization for ranged hash function while caching hash codes.
-  // That combination is meaningless, and trying to do it is an error.
-
-  /// Specialization: ranged hash function, cache hash codes.  This
-  /// combination is meaningless, so we provide only a declaration
-  /// and no definition.
-  template<typename _Key, typename _Value, typename _ExtractKey,
-	   typename _H1, typename _H2, typename _Hash>
-    struct _Hash_code_base<_Key, _Value, _ExtractKey, _H1, _H2, _Hash,
-			   __hash_cached_t>;
-
   /// Specialization: hash function and range-hashing function, no
   /// caching of hash codes.
   /// Provides typedef and accessor required by C++ 11.
   template<typename _Key, typename _Value, typename _ExtractKey,
-	   typename _H1, typename _H2>
-    struct _Hash_code_base<_Key, _Value, _ExtractKey, _H1, _H2,
-			   _Default_ranged_hash, __hash_not_cached_t>
+	   typename _Hash, typename _RangedHash>
+    struct _Hash_code_base<_Key, _Value, _ExtractKey, _Hash, _RangedHash,
+			   __hash_not_cached_t>
     : private _Hashtable_ebo_helper<0, _ExtractKey>,
-      private _Hashtable_ebo_helper<1, _H1>,
-      private _Hashtable_ebo_helper<2, _H2>
+      private _Hashtable_ebo_helper<1, _Hash>
     {
     private:
       using __ebo_extract_key = _Hashtable_ebo_helper<0, _ExtractKey>;
-      using __ebo_h1 = _Hashtable_ebo_helper<1, _H1>;
-      using __ebo_h2 = _Hashtable_ebo_helper<2, _H2>;
+      using __ebo_hash = _Hashtable_ebo_helper<1, _Hash>;
 
       // Gives the local iterator implementation access to _M_bucket_index().
-      friend struct _Local_iterator_base<_Key, _Value, _ExtractKey, _H1, _H2,
-					 _Default_ranged_hash,
+      friend struct _Local_iterator_base<_Key, _Value, _ExtractKey,
+					 _Hash, _RangedHash,
 					 __hash_not_cached_t>;
 
     public:
-      typedef _H1 					hasher;
+      typedef _Hash					hasher;
 
       hasher
       hash_function() const
-      { return _M_h1(); }
+      { return _M_hash(); }
 
     protected:
       typedef std::size_t 				__hash_code;
@@ -1281,30 +1178,29 @@  namespace __detail
       // default constructor.
       _Hash_code_base() = default;
 
-      _Hash_code_base(const _ExtractKey& __ex,
-		      const _H1& __h1, const _H2& __h2,
-		      const _Default_ranged_hash&)
-      : __ebo_extract_key(__ex), __ebo_h1(__h1), __ebo_h2(__h2) { }
+      _Hash_code_base(const _ExtractKey& __ex, const _Hash& __hash)
+      : __ebo_extract_key(__ex), __ebo_hash(__hash) { }
 
       __hash_code
       _M_hash_code(const _Key& __k) const
       {
-	static_assert(__is_invocable<const _H1&, const _Key&>{},
+	static_assert(__is_invocable<const _Hash&, const _Key&>{},
 	    "hash function must be invocable with an argument of key type");
-	return _M_h1()(__k);
+	return _M_hash()(__k);
       }
 
       std::size_t
-      _M_bucket_index(const _Key&, __hash_code __c,
-		      std::size_t __bkt_count) const
-      { return _M_h2()(__c, __bkt_count); }
+      _M_bucket_index(__hash_code __c, std::size_t __bkt_count) const
+      { return _RangedHash{}(__c, __bkt_count); }
 
       std::size_t
       _M_bucket_index(const __node_type* __p, std::size_t __bkt_count) const
-	noexcept( noexcept(declval<const _H1&>()(declval<const _Key&>()))
-		  && noexcept(declval<const _H2&>()((__hash_code)0,
-						    (std::size_t)0)) )
-      { return _M_h2()(_M_h1()(_M_extract()(__p->_M_v())), __bkt_count); }
+	noexcept( noexcept(declval<const _Hash&>()(declval<const _Key&>()))
+		  && noexcept(declval<const _RangedHash&>()((__hash_code)0,
+							   (std::size_t)0)) )
+      {
+	return _RangedHash{}(_M_hash()(_M_extract()(__p->_M_v())), __bkt_count);
+      }
 
       void
       _M_store_code(__node_type*, __hash_code) const
@@ -1319,46 +1215,41 @@  namespace __detail
       {
 	std::swap(__ebo_extract_key::_M_get(),
 		  __x.__ebo_extract_key::_M_get());
-	std::swap(__ebo_h1::_M_get(), __x.__ebo_h1::_M_get());
-	std::swap(__ebo_h2::_M_get(), __x.__ebo_h2::_M_get());
+	std::swap(__ebo_hash::_M_get(), __x.__ebo_hash::_M_get());
       }
 
       const _ExtractKey&
       _M_extract() const { return __ebo_extract_key::_M_cget(); }
 
-      const _H1&
-      _M_h1() const { return __ebo_h1::_M_cget(); }
-
-      const _H2&
-      _M_h2() const { return __ebo_h2::_M_cget(); }
+      const _Hash&
+      _M_hash() const { return __ebo_hash::_M_cget(); }
     };
 
   /// Specialization: hash function and range-hashing function,
   /// caching hash codes.  H is provided but ignored.  Provides
   /// typedef and accessor required by C++ 11.
   template<typename _Key, typename _Value, typename _ExtractKey,
-	   typename _H1, typename _H2>
-    struct _Hash_code_base<_Key, _Value, _ExtractKey, _H1, _H2,
-			   _Default_ranged_hash, __hash_cached_t>
+	   typename _Hash, typename _RangedHash>
+    struct _Hash_code_base<_Key, _Value, _ExtractKey, _Hash, _RangedHash,
+			   __hash_cached_t>
     : private _Hashtable_ebo_helper<0, _ExtractKey>,
-      private _Hashtable_ebo_helper<1, _H1>,
-      private _Hashtable_ebo_helper<2, _H2>
+      private _Hashtable_ebo_helper<1, _Hash>
     {
     private:
       // Gives the local iterator implementation access to _M_h2().
-      friend struct _Local_iterator_base<_Key, _Value, _ExtractKey, _H1, _H2,
-					 _Default_ranged_hash, __hash_cached_t>;
+      friend struct _Local_iterator_base<_Key, _Value, _ExtractKey,
+					 _Hash, _RangedHash,
+					 __hash_cached_t>;
 
       using __ebo_extract_key = _Hashtable_ebo_helper<0, _ExtractKey>;
-      using __ebo_h1 = _Hashtable_ebo_helper<1, _H1>;
-      using __ebo_h2 = _Hashtable_ebo_helper<2, _H2>;
+      using __ebo_hash = _Hashtable_ebo_helper<1, _Hash>;
 
     public:
-      typedef _H1 					hasher;
+      typedef _Hash					hasher;
 
       hasher
       hash_function() const
-      { return _M_h1(); }
+      { return _M_hash(); }
 
     protected:
       typedef std::size_t 				__hash_code;
@@ -1366,29 +1257,26 @@  namespace __detail
 
       // We need the default constructor for _Hashtable default constructor.
       _Hash_code_base() = default;
-      _Hash_code_base(const _ExtractKey& __ex,
-		      const _H1& __h1, const _H2& __h2,
-		      const _Default_ranged_hash&)
-      : __ebo_extract_key(__ex), __ebo_h1(__h1), __ebo_h2(__h2) { }
+      _Hash_code_base(const _ExtractKey& __ex, const _Hash& __hash)
+      : __ebo_extract_key(__ex), __ebo_hash(__hash) { }
 
       __hash_code
       _M_hash_code(const _Key& __k) const
       {
-	static_assert(__is_invocable<const _H1&, const _Key&>{},
+	static_assert(__is_invocable<const _Hash&, const _Key&>{},
 	    "hash function must be invocable with an argument of key type");
-	return _M_h1()(__k);
+	return _M_hash()(__k);
       }
 
       std::size_t
-      _M_bucket_index(const _Key&, __hash_code __c,
-		      std::size_t __bkt_count) const
-      { return _M_h2()(__c, __bkt_count); }
+      _M_bucket_index(__hash_code __c, std::size_t __bkt_count) const
+      { return _RangedHash{}(__c, __bkt_count); }
 
       std::size_t
       _M_bucket_index(const __node_type* __p, std::size_t __bkt_count) const
-	noexcept( noexcept(declval<const _H2&>()((__hash_code)0,
-						 (std::size_t)0)) )
-      { return _M_h2()(__p->_M_hash_code, __bkt_count); }
+	noexcept( noexcept(declval<const _RangedHash&>()((__hash_code)0,
+							(std::size_t)0)) )
+      { return _RangedHash{}(__p->_M_hash_code, __bkt_count); }
 
       void
       _M_store_code(__node_type* __n, __hash_code __c) const
@@ -1403,41 +1291,36 @@  namespace __detail
       {
 	std::swap(__ebo_extract_key::_M_get(),
 		  __x.__ebo_extract_key::_M_get());
-	std::swap(__ebo_h1::_M_get(), __x.__ebo_h1::_M_get());
-	std::swap(__ebo_h2::_M_get(), __x.__ebo_h2::_M_get());
+	std::swap(__ebo_hash::_M_get(), __x.__ebo_hash::_M_get());
       }
 
       const _ExtractKey&
       _M_extract() const { return __ebo_extract_key::_M_cget(); }
 
-      const _H1&
-      _M_h1() const { return __ebo_h1::_M_cget(); }
-
-      const _H2&
-      _M_h2() const { return __ebo_h2::_M_cget(); }
+      const _Hash&
+      _M_hash() const { return __ebo_hash::_M_cget(); }
     };
 
   /// Partial specialization used when nodes contain a cached hash code.
   template<typename _Key, typename _Value, typename _ExtractKey,
-	   typename _H1, typename _H2, typename _Hash>
+	   typename _Hash, typename _RangedHash>
     struct _Local_iterator_base<_Key, _Value, _ExtractKey,
-				_H1, _H2, _Hash, __hash_cached_t>
-    : private _Hashtable_ebo_helper<0, _H2>
-    , _Node_iterator_base<_Value, __hash_cached_t>
+				_Hash, _RangedHash, __hash_cached_t>
+    : public _Node_iterator_base<_Value, __hash_cached_t>
     {
     protected:
-      using __base_type = _Hashtable_ebo_helper<0, _H2>;
-      using __base_node_iter = _Node_iterator_base<_Value, __hash_cached_t>;
-      using __hash_code_base = _Hash_code_base<_Key, _Value, _ExtractKey,
-					       _H1, _H2, _Hash,
-					       __hash_cached_t>;
+      using __hash_code_base =
+	_Hash_code_base<_Key, _Value, _ExtractKey, _Hash, _RangedHash,
+			__hash_cached_t>;
+      using __base_node_iter =
+	_Node_iterator_base<_Value, __hash_cached_t>;
 
       _Local_iterator_base() = default;
-      _Local_iterator_base(const __hash_code_base& __base,
+      _Local_iterator_base(const __hash_code_base&,
 			   _Hash_node<_Value, __hash_cached_t>* __p,
 			   std::size_t __bkt, std::size_t __bkt_count)
-      : __base_type(__base._M_h2()), __base_node_iter(__p)
-      , _M_bucket(__bkt), _M_bucket_count(__bkt_count) { }
+      : __base_node_iter(__p), _M_bucket(__bkt), _M_bucket_count(__bkt_count)
+      { }
 
       void
       _M_incr()
@@ -1446,8 +1329,7 @@  namespace __detail
 	if (this->_M_cur)
 	  {
 	    std::size_t __bkt
-	      = __base_type::_M_get()(this->_M_cur->_M_hash_code,
-				      _M_bucket_count);
+	      = _RangedHash{}(this->_M_cur->_M_hash_code, _M_bucket_count);
 	    if (__bkt != _M_bucket)
 	      this->_M_cur = nullptr;
 	  }
@@ -1493,23 +1375,23 @@  namespace __detail
     };
 
   template<typename _Key, typename _Value, typename _ExtractKey,
-	   typename _H1, typename _H2, typename _Hash>
+	   typename _Hash, typename _RangedHash>
     using __hash_code_for_local_iter
       = _Hash_code_storage<_Hash_code_base<_Key, _Value, _ExtractKey,
-					   _H1, _H2, _Hash,
+					   _Hash, _RangedHash,
 					   __hash_not_cached_t>>;
 
   // Partial specialization used when hash codes are not cached
   template<typename _Key, typename _Value, typename _ExtractKey,
-	   typename _H1, typename _H2, typename _Hash>
+	   typename _Hash, typename _RangedHash>
     struct _Local_iterator_base<_Key, _Value, _ExtractKey,
-				_H1, _H2, _Hash, __hash_not_cached_t>
-    : __hash_code_for_local_iter<_Key, _Value, _ExtractKey, _H1, _H2, _Hash>
+				_Hash, _RangedHash, __hash_not_cached_t>
+    : __hash_code_for_local_iter<_Key, _Value, _ExtractKey, _Hash, _RangedHash>
     , _Node_iterator_base<_Value, __hash_not_cached_t>
     {
     protected:
       using __hash_code_base = _Hash_code_base<_Key, _Value, _ExtractKey,
-					       _H1, _H2, _Hash,
+					       _Hash, _RangedHash,
 					       __hash_not_cached_t>;
       using __node_iter_base = _Node_iterator_base<_Value, __hash_not_cached_t>;
 
@@ -1578,15 +1460,16 @@  namespace __detail
 
   /// local iterators
   template<typename _Key, typename _Value, typename _ExtractKey,
-	   typename _H1, typename _H2, typename _Hash,
+	   typename _Hash, typename _RangedHash,
 	   typename _Constant_iterators, typename _Cache_hash_code>
     struct _Local_iterator
     : public _Local_iterator_base<_Key, _Value, _ExtractKey,
-				  _H1, _H2, _Hash, _Cache_hash_code>
+				  _Hash, _RangedHash, _Cache_hash_code>
     {
     private:
-      using __base_type = _Local_iterator_base<_Key, _Value, _ExtractKey,
-					       _H1, _H2, _Hash, _Cache_hash_code>;
+      using __base_type =
+	_Local_iterator_base<_Key, _Value, _ExtractKey, _Hash, _RangedHash,
+			     _Cache_hash_code>;
       using __hash_code_base = typename __base_type::__hash_code_base;
 
     public:
@@ -1634,15 +1517,15 @@  namespace __detail
 
   /// local const_iterators
   template<typename _Key, typename _Value, typename _ExtractKey,
-	   typename _H1, typename _H2, typename _Hash,
+	   typename _Hash, typename _RangedHash,
 	   typename _Constant_iterators, typename _Cache_hash_code>
     struct _Local_const_iterator
     : public _Local_iterator_base<_Key, _Value, _ExtractKey,
-				  _H1, _H2, _Hash, _Cache_hash_code>
+				  _Hash, _RangedHash, _Cache_hash_code>
     {
     private:
       using __base_type = _Local_iterator_base<_Key, _Value, _ExtractKey,
-					       _H1, _H2, _Hash,
+					       _Hash, _RangedHash,
 					       _Cache_hash_code>;
       using __hash_code_base = typename __base_type::__hash_code_base;
 
@@ -1662,7 +1545,7 @@  namespace __detail
       { }
 
       _Local_const_iterator(const _Local_iterator<_Key, _Value, _ExtractKey,
-						  _H1, _H2, _Hash,
+						  _Hash, _RangedHash,
 						  _Constant_iterators,
 						  _Cache_hash_code>& __x)
       : __base_type(__x)
@@ -1704,9 +1587,9 @@  namespace __detail
    */
   template<typename _Key, typename _Value,
 	   typename _ExtractKey, typename _Equal,
-	   typename _H1, typename _H2, typename _Hash, typename _Traits>
+	   typename _Hash, typename _RangedHash, typename _Traits>
   struct _Hashtable_base
-  : public _Hash_code_base<_Key, _Value, _ExtractKey, _H1, _H2, _Hash,
+  : public _Hash_code_base<_Key, _Value, _ExtractKey, _Hash, _RangedHash,
 			   typename _Traits::__hash_cached>,
     private _Hashtable_ebo_helper<0, _Equal>
   {
@@ -1723,30 +1606,29 @@  namespace __detail
     using __unique_keys = typename __traits_type::__unique_keys;
 
     using __hash_code_base = _Hash_code_base<_Key, _Value, _ExtractKey,
-					     _H1, _H2, _Hash,
+					     _Hash, _RangedHash,
 					     __hash_cached>;
 
     using __hash_code = typename __hash_code_base::__hash_code;
     using __node_type = typename __hash_code_base::__node_type;
 
-    using iterator = __detail::_Node_iterator<value_type,
-					      __constant_iterators,
-					      __hash_cached>;
+    using iterator =
+      __detail::_Node_iterator<value_type,
+			       __constant_iterators, __hash_cached>;
 
-    using const_iterator = __detail::_Node_const_iterator<value_type,
-						   __constant_iterators,
-						   __hash_cached>;
+    using const_iterator =
+      __detail::_Node_const_iterator<value_type,
+				     __constant_iterators, __hash_cached>;
 
-    using local_iterator = __detail::_Local_iterator<key_type, value_type,
-						   _ExtractKey, _H1, _H2, _Hash,
-						     __constant_iterators,
-						     __hash_cached>;
+    using local_iterator =
+      __detail::_Local_iterator<key_type, value_type,
+				_ExtractKey, _Hash, _RangedHash,
+				__constant_iterators, __hash_cached>;
 
-    using const_local_iterator = __detail::_Local_const_iterator<key_type,
-								 value_type,
-					_ExtractKey, _H1, _H2, _Hash,
-					__constant_iterators,
-					__hash_cached>;
+    using const_local_iterator =
+      __detail::_Local_const_iterator<key_type, value_type,
+				      _ExtractKey, _Hash, _RangedHash,
+				      __constant_iterators, __hash_cached>;
 
     using __ireturn_type = typename std::conditional<__unique_keys::value,
 						     std::pair<iterator, bool>,
@@ -1781,9 +1663,9 @@  namespace __detail
 
   protected:
     _Hashtable_base() = default;
-    _Hashtable_base(const _ExtractKey& __ex, const _H1& __h1, const _H2& __h2,
+    _Hashtable_base(const _ExtractKey& __ex,
 		    const _Hash& __hash, const _Equal& __eq)
-    : __hash_code_base(__ex, __h1, __h2, __hash), _EqualEBO(__eq)
+    : __hash_code_base(__ex, __hash), _EqualEBO(__eq)
     { }
 
     bool
@@ -1884,21 +1766,20 @@  namespace __detail
    */
   template<typename _Key, typename _Value, typename _Alloc,
 	   typename _ExtractKey, typename _Equal,
-	   typename _H1, typename _H2, typename _Hash,
-	   typename _RehashPolicy, typename _Traits,
+	   typename _Hash, typename _RehashPolicy,
+	   typename _Traits,
 	   typename = typename _Traits::__unique_keys>
     struct _Equality;
 
   /// Specialization.
   template<typename _Key, typename _Value, typename _Alloc,
 	   typename _ExtractKey, typename _Equal,
-	   typename _H1, typename _H2, typename _Hash,
-	   typename _RehashPolicy, typename _Traits>
+	   typename _Hash, typename _RehashPolicy, typename _Traits>
     struct _Equality<_Key, _Value, _Alloc, _ExtractKey, _Equal,
-		     _H1, _H2, _Hash, _RehashPolicy, _Traits, __unique_keys_t>
+		     _Hash, _RehashPolicy, _Traits, __unique_keys_t>
     {
       using __hashtable = _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
-				     _H1, _H2, _Hash, _RehashPolicy, _Traits>;
+				     _Hash, _RehashPolicy, _Traits>;
 
       bool
       _M_equal(const __hashtable&) const;
@@ -1906,11 +1787,10 @@  namespace __detail
 
   template<typename _Key, typename _Value, typename _Alloc,
 	   typename _ExtractKey, typename _Equal,
-	   typename _H1, typename _H2, typename _Hash,
-	   typename _RehashPolicy, typename _Traits>
+	   typename _Hash, typename _RehashPolicy, typename _Traits>
     bool
     _Equality<_Key, _Value, _Alloc, _ExtractKey, _Equal,
-	      _H1, _H2, _Hash, _RehashPolicy, _Traits, __unique_keys_t>::
+	      _Hash, _RehashPolicy, _Traits, __unique_keys_t>::
     _M_equal(const __hashtable& __other) const
     {
       const __hashtable* __this = static_cast<const __hashtable*>(this);
@@ -1930,14 +1810,13 @@  namespace __detail
   /// Specialization.
   template<typename _Key, typename _Value, typename _Alloc,
 	   typename _ExtractKey, typename _Equal,
-	   typename _H1, typename _H2, typename _Hash,
-	   typename _RehashPolicy, typename _Traits>
+	   typename _Hash, typename _RehashPolicy, typename _Traits>
     struct _Equality<_Key, _Value, _Alloc, _ExtractKey, _Equal,
-		     _H1, _H2, _Hash, _RehashPolicy, _Traits, __multi_keys_t>
+		     _Hash, _RehashPolicy, _Traits, __multi_keys_t>
     : public _Equality_base
     {
       using __hashtable = _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
-				     _H1, _H2, _Hash, _RehashPolicy, _Traits>;
+				     _Hash, _RehashPolicy, _Traits>;
 
       bool
       _M_equal(const __hashtable&) const;
@@ -1945,11 +1824,10 @@  namespace __detail
 
   template<typename _Key, typename _Value, typename _Alloc,
 	   typename _ExtractKey, typename _Equal,
-	   typename _H1, typename _H2, typename _Hash,
-	   typename _RehashPolicy, typename _Traits>
+	   typename _Hash, typename _RehashPolicy, typename _Traits>
     bool
     _Equality<_Key, _Value, _Alloc, _ExtractKey, _Equal,
-	      _H1, _H2, _Hash, _RehashPolicy, _Traits, __multi_keys_t>::
+	      _Hash, _RehashPolicy, _Traits, __multi_keys_t>::
     _M_equal(const __hashtable& __other) const
     {
       const __hashtable* __this = static_cast<const __hashtable*>(this);
diff --git a/libstdc++-v3/include/bits/node_handle.h b/libstdc++-v3/include/bits/node_handle.h
index 2d134c1ba2c..013d9d7dbbf 100644
--- a/libstdc++-v3/include/bits/node_handle.h
+++ b/libstdc++-v3/include/bits/node_handle.h
@@ -226,8 +226,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       template<typename _Key2, typename _Value2, typename _ValueAlloc,
 	       typename _ExtractKey, typename _Equal,
-	       typename _H1, typename _H2, typename _Hash,
-	       typename _RehashPolicy, typename _Traits>
+	       typename _Hash, typename _RehashPolicy, typename _Traits>
 	friend class _Hashtable;
     };
 
@@ -278,8 +277,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       template<typename _Key2, typename _Value2, typename _ValueAlloc,
 	       typename _ExtractKey, typename _Equal,
-	       typename _H1, typename _H2, typename _Hash,
-	       typename _RehashPolicy, typename _Traits>
+	       typename _Hash, typename _RehashPolicy, typename _Traits>
 	friend class _Hashtable;
     };
 
diff --git a/libstdc++-v3/include/bits/unordered_map.h b/libstdc++-v3/include/bits/unordered_map.h
index 5131e02e8aa..310cfd39d79 100644
--- a/libstdc++-v3/include/bits/unordered_map.h
+++ b/libstdc++-v3/include/bits/unordered_map.h
@@ -48,8 +48,6 @@  _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
     using __umap_hashtable = _Hashtable<_Key, std::pair<const _Key, _Tp>,
                                         _Alloc, __detail::_Select1st,
 				        _Pred, _Hash,
-				        __detail::_Mod_range_hashing,
-				        __detail::_Default_ranged_hash,
 				        __detail::_Prime_rehash_policy, _Tr>;
 
   /// Base types for unordered_multimap.
@@ -65,8 +63,6 @@  _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
     using __ummap_hashtable = _Hashtable<_Key, std::pair<const _Key, _Tp>,
 					 _Alloc, __detail::_Select1st,
 					 _Pred, _Hash,
-					 __detail::_Mod_range_hashing,
-					 __detail::_Default_ranged_hash,
 					 __detail::_Prime_rehash_policy, _Tr>;
 
   template<class _Key, class _Tp, class _Hash, class _Pred, class _Alloc>
diff --git a/libstdc++-v3/include/bits/unordered_set.h b/libstdc++-v3/include/bits/unordered_set.h
index 7154ec843db..4319495f18b 100644
--- a/libstdc++-v3/include/bits/unordered_set.h
+++ b/libstdc++-v3/include/bits/unordered_set.h
@@ -46,8 +46,6 @@  _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
 	   typename _Tr = __uset_traits<__cache_default<_Value, _Hash>::value>>
     using __uset_hashtable = _Hashtable<_Value, _Value, _Alloc,
 					__detail::_Identity, _Pred, _Hash,
-					__detail::_Mod_range_hashing,
-					__detail::_Default_ranged_hash,
 					__detail::_Prime_rehash_policy, _Tr>;
 
   /// Base types for unordered_multiset.
@@ -62,8 +60,6 @@  _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
     using __umset_hashtable = _Hashtable<_Value, _Value, _Alloc,
 					 __detail::_Identity,
 					 _Pred, _Hash,
-					 __detail::_Mod_range_hashing,
-					 __detail::_Default_ranged_hash,
 					 __detail::_Prime_rehash_policy, _Tr>;
 
   template<class _Value, class _Hash, class _Pred, class _Alloc>
diff --git a/libstdc++-v3/testsuite/23_containers/unordered_set/hash_policy/26132.cc b/libstdc++-v3/testsuite/23_containers/unordered_set/hash_policy/26132.cc
index dd3a4fd9b76..1fd87693bed 100644
--- a/libstdc++-v3/testsuite/23_containers/unordered_set/hash_policy/26132.cc
+++ b/libstdc++-v3/testsuite/23_containers/unordered_set/hash_policy/26132.cc
@@ -55,8 +55,6 @@  template<typename _Value>
 		  std::__detail::_Identity,
 		  std::equal_to<_Value>,
 		  std::hash<_Value>,
-		  std::__detail::_Mask_range_hashing,
-		  std::__detail::_Default_ranged_hash,
 		  std::__detail::_Power2_rehash_policy,
 		  std::__detail::_Hashtable_traits<false, true, true>>;
 
diff --git a/libstdc++-v3/testsuite/23_containers/unordered_set/hash_policy/71181.cc b/libstdc++-v3/testsuite/23_containers/unordered_set/hash_policy/71181.cc
index 7bbf4fd73db..1f6caa36700 100644
--- a/libstdc++-v3/testsuite/23_containers/unordered_set/hash_policy/71181.cc
+++ b/libstdc++-v3/testsuite/23_containers/unordered_set/hash_policy/71181.cc
@@ -49,8 +49,6 @@  template<typename _Value>
 		  std::__detail::_Identity,
 		  std::equal_to<_Value>,
 		  std::hash<_Value>,
-		  std::__detail::_Mask_range_hashing,
-		  std::__detail::_Default_ranged_hash,
 		  std::__detail::_Power2_rehash_policy,
 		  std::__detail::_Hashtable_traits<false, true, true>>;
 
diff --git a/libstdc++-v3/testsuite/23_containers/unordered_set/hash_policy/load_factor.cc b/libstdc++-v3/testsuite/23_containers/unordered_set/hash_policy/load_factor.cc
index 2a9dd745bcc..d68fac7697e 100644
--- a/libstdc++-v3/testsuite/23_containers/unordered_set/hash_policy/load_factor.cc
+++ b/libstdc++-v3/testsuite/23_containers/unordered_set/hash_policy/load_factor.cc
@@ -58,8 +58,6 @@  template<typename _Value>
 		  std::__detail::_Identity,
 		  std::equal_to<_Value>,
 		  std::hash<_Value>,
-		  std::__detail::_Mask_range_hashing,
-		  std::__detail::_Default_ranged_hash,
 		  std::__detail::_Power2_rehash_policy,
 		  std::__detail::_Hashtable_traits<false, true, true>>;
 
diff --git a/libstdc++-v3/testsuite/23_containers/unordered_set/hash_policy/rehash.cc b/libstdc++-v3/testsuite/23_containers/unordered_set/hash_policy/rehash.cc
index d717b0c7def..a134b353889 100644
--- a/libstdc++-v3/testsuite/23_containers/unordered_set/hash_policy/rehash.cc
+++ b/libstdc++-v3/testsuite/23_containers/unordered_set/hash_policy/rehash.cc
@@ -62,8 +62,6 @@  template<typename _Value>
 		  std::__detail::_Identity,
 		  std::equal_to<_Value>,
 		  std::hash<_Value>,
-		  std::__detail::_Mask_range_hashing,
-		  std::__detail::_Default_ranged_hash,
 		  std::__detail::_Power2_rehash_policy,
 		  std::__detail::_Hashtable_traits<false, true, true>>;
 
diff --git a/libstdc++-v3/testsuite/23_containers/unordered_set/insert/hash_policy.cc b/libstdc++-v3/testsuite/23_containers/unordered_set/insert/hash_policy.cc
index 89cda8daa8d..54e5635684f 100644
--- a/libstdc++-v3/testsuite/23_containers/unordered_set/insert/hash_policy.cc
+++ b/libstdc++-v3/testsuite/23_containers/unordered_set/insert/hash_policy.cc
@@ -122,8 +122,6 @@  template<typename _Value, typename _Hash,
 		  std::__detail::_Identity,
 		  _Pred,
 		  _Hash,
-		  std::__detail::_Mask_range_hashing,
-		  std::__detail::_Default_ranged_hash,
 		  std::__detail::_Power2_rehash_policy,
 		  std::__detail::_Hashtable_traits<false, true, true>>;
 
diff --git a/libstdc++-v3/testsuite/23_containers/unordered_set/max_load_factor/robustness.cc b/libstdc++-v3/testsuite/23_containers/unordered_set/max_load_factor/robustness.cc
index 47cbadeb746..c36c9f6f977 100644
--- a/libstdc++-v3/testsuite/23_containers/unordered_set/max_load_factor/robustness.cc
+++ b/libstdc++-v3/testsuite/23_containers/unordered_set/max_load_factor/robustness.cc
@@ -84,8 +84,6 @@  template<typename _Value, typename _Hash,
 		  std::__detail::_Identity,
 		  _Pred,
 		  _Hash,
-		  std::__detail::_Mask_range_hashing,
-		  std::__detail::_Default_ranged_hash,
 		  std::__detail::_Power2_rehash_policy,
 		  std::__detail::_Hashtable_traits<false, true, true>>;
 
diff --git a/libstdc++-v3/testsuite/performance/23_containers/insert/54075.cc b/libstdc++-v3/testsuite/performance/23_containers/insert/54075.cc
index 4e9b6e01a87..a632f659a1f 100644
--- a/libstdc++-v3/testsuite/performance/23_containers/insert/54075.cc
+++ b/libstdc++-v3/testsuite/performance/23_containers/insert/54075.cc
@@ -134,8 +134,6 @@  template<bool cache>
 	      std::_Hashtable<Foo, Foo, std::allocator<Foo>,
 			      std::__detail::_Identity,
 			      std::equal_to<Foo>, HashFunction,
-			      std::__detail::_Mask_range_hashing,
-			      std::__detail::_Default_ranged_hash,
 			      std::__detail::_Power2_rehash_policy,
 			      std::__uset_traits<cache>>;
 
@@ -144,8 +142,6 @@  template<bool cache>
 	      std::_Hashtable<Foo, Foo, std::allocator<Foo>,
 			      std::__detail::_Identity,
 			      std::equal_to<Foo>, HashFunction,
-			      std::__detail::_Mask_range_hashing,
-			      std::__detail::_Default_ranged_hash,
 			      std::__detail::_Power2_rehash_policy,
 			      std::__umset_traits<cache>>;
 
diff --git a/libstdc++-v3/testsuite/performance/23_containers/insert_erase/41975.cc b/libstdc++-v3/testsuite/performance/23_containers/insert_erase/41975.cc
index b6b303d853c..5bf05ee9587 100644
--- a/libstdc++-v3/testsuite/performance/23_containers/insert_erase/41975.cc
+++ b/libstdc++-v3/testsuite/performance/23_containers/insert_erase/41975.cc
@@ -181,8 +181,6 @@  template<bool cache>
 	      std::_Hashtable<int, int, std::allocator<int>,
 			      std::__detail::_Identity,
 			      std::equal_to<int>, std::hash<int>,
-			      std::__detail::_Mask_range_hashing,
-			      std::__detail::_Default_ranged_hash,
 			      std::__detail::_Power2_rehash_policy,
 			      std::__uset_traits<cache>>;
 
@@ -205,8 +203,6 @@  template<bool cache>
 	      std::_Hashtable<std::string, std::string, std::allocator<std::string>,
 			      std::__detail::_Identity,
 			      std::equal_to<std::string>, std::hash<std::string>,
-			      std::__detail::_Mask_range_hashing,
-			      std::__detail::_Default_ranged_hash,
 			      std::__detail::_Power2_rehash_policy,
 			      std::__uset_traits<cache>>;
 
diff --git a/libstdc++-v3/testsuite/performance/23_containers/insert_erase/unordered_small_size.cc b/libstdc++-v3/testsuite/performance/23_containers/insert_erase/unordered_small_size.cc
index ca589ad85b7..3514343e7df 100644
--- a/libstdc++-v3/testsuite/performance/23_containers/insert_erase/unordered_small_size.cc
+++ b/libstdc++-v3/testsuite/performance/23_containers/insert_erase/unordered_small_size.cc
@@ -206,8 +206,6 @@  template<bool cache>
 	      std::_Hashtable<int, int, std::allocator<int>,
 			      std::__detail::_Identity,
 			      std::equal_to<int>, std::hash<int>,
-			      std::__detail::_Mask_range_hashing,
-			      std::__detail::_Default_ranged_hash,
 			      std::__detail::_Power2_rehash_policy,
 			      std::__uset_traits<cache>>;
 
@@ -230,8 +228,6 @@  template<bool cache>
 	      std::_Hashtable<std::string, std::string, std::allocator<std::string>,
 			      std::__detail::_Identity,
 			      std::equal_to<std::string>, std::hash<std::string>,
-			      std::__detail::_Mask_range_hashing,
-			      std::__detail::_Default_ranged_hash,
 			      std::__detail::_Power2_rehash_policy,
 			      std::__uset_traits<cache>>;