1. Motivation and Scope
Heterogeneous lookup allows unordered associative containers (std :: unordered_map std :: unordered_set Hash Pred is_transparent transparent_key_equal is_transparent Pred Hash :: transparent_key_equal Hash :: transparent_key_equal is_transparent 2. Design Concerns
2.1. Consistency with Prior Art and Existing Practices
[N3657] introduced heterogeneous lookup support for ordered associative containers (std :: map std :: set Compare :: is_transparent Hash :: transparent_key_equal is_transparent Pred [P0919R3] does not standardize existing practice. [SwissTables] and [F14] Hash Tables, two of the most widely used C++ hash table implementations in
Google
and Facebook, both use the conjunction of 
2.2. Compatibility of Hash Pred 
    LEWG raised a valid concern about the compatibility of the Hash Pred is_transparent Hash Pred [P0919R3] tries to address the compatibility concern by stipulating
compilation failures for containers with incompatible 
We argue that the compatibility concern can be addressed by the implementation via good defaults, compiler warnings, and existing compilation rules. In our experience, this has not posed a problem.
2.3. Minimizing User Confusion
With [P0919R3], the user could specify the key equality comparator in two places: as thePred Hash :: transparent_key_equal Pred transparent_key_equal std :: equal_to < Key > Pred Pred MyEqualTo std :: equal_to struct MyEqualTo { using is_transparent = ...; ... }; struct MyHash { using is_transparent = ...; using transparent_key_equal = MyEqualTo ; ... }; // The user explicitly uses std::equal_to<K> as the 4th template parameter, but // in fact that type is NEVER used: std :: unordered_map < K , V , MyHash , std :: equal_to < K >> m ; 
In addition, allowing the 
CommonHasher Pred struct CommonEqualTo {...}; struct CommonHasher { using is_transparent = void ; using transparent_key_equal = CommonEqualTo ; ... }; std :: unordered_set < K , CommonHasher > s1 ; // works just fine struct MyEqualTo {...}; std :: unordered_set < K , CommonHasher , MyEqualTo > s2 ; // fails to compile 
3. Impact On The Standard
This proposal modifies the unordered associative containers in< unordered_map > < unordered_set > There are no language changes.
Almost all existing C++17 code is unaffected because new member functions are
disabled from overload resolution process unless both the 
4. Proposed Wording
The proposed changes are relative to the working draft of the standard as of [N4810].
Modify 22.2.7 [unord.req] paragraph 11.7 as follows:
(11.7)denotes a possiblya_tran value of typeconst when theX the qualified-idqualified-idsis valid and denotes a type (13.9.2),X :: hasher :: transparent_key_equal andX :: key_equal :: is_transparent are both valid and denote types (13.9.2),X :: hasher :: is_transparent 
Modify table 70 in section 22.2.7 [unord.req] as follows:
Expression Return type Assertion/note pre-/post-condition Complexity X :: key_equal if such a qualified-id is valid and denotes a type (13.9.2); otherwise,Hash :: transparent_key_equal .Pred Pred Requires: iskey_equal .CopyConstructible shall be a binary predicate that takes two arguments of typekey_equal .Key is an equivalence relation.key_equal compile time 
Modify paragraph 17 in 22.2.7 [unord.req]:
If the qualified-idThe member function templatesis valid and denotes a type (12.9.2), then the program is ill-formed if either:Hash :: transparent_key_equal 
qualified-id
is not valid or does not denote a type, orHash :: transparent_key_equal :: is_transparent 
is a different type thanPred orequal_to < Key > .Hash :: transparent_key_equal ,find ,count , andequal_range shall not participate in overload resolution unless thecontains qualified-idqualified-idsis valid and denotes a typeHash :: transparent_key_equal andPred :: is_transparent are both valid and denote types (13.9.2).Hash :: is_transparent 
Modify paragraph 3 of 22.5.4.1 [unord.map.overview] as follows:
namespace std { template < class Key , class T , class Hash = hash < Key > , class Pred = equal_to < Key > , class Allocator = allocator < pair < const Key , T >>> class unordered_map { public : // types using key_type = Key ; using mapped_type = T ; using value_type = pair < const Key , T > ; using hasher = Hash ; using key_equal = see 22.2.7 ; using key_equal = Pred ; using allocator_type = Allocator ; 
Modify paragraph 3 of 22.5.5.1 [unord.multimap.overview] as follows:
namespace std { template < class Key , class T , class Hash = hash < Key > , class Pred = equal_to < Key > , class Allocator = allocator < pair < const Key , T >>> class unordered_multimap { public : // types using key_type = Key ; using mapped_type = T ; using value_type = pair < const Key , T > ; using hasher = Hash ; using key_equal = see 22.2.7 ; using key_equal = Pred ; using allocator_type = Allocator ; 
Modify paragraph 3 of 22.5.6.1 [unord.set.overview] add:
namespace std { template < class Key , class Hash = hash < Key > , class Pred = equal_to < Key > , class Allocator = allocator < pair < const Key , T >>> class unordered_set { public : // types using key_type = Key ; using value_type = Key ; using hasher = Hash ; using key_equal = see 22.2.7 ; using key_equal = Pred ; using allocator_type = Allocator ; 
Modify paragraph 3 of 22.5.7.1 [unord.multiset.overview] add:
namespace std { template < class Key , class Hash = hash < Key > , class Pred = equal_to < Key > , class Allocator = allocator < pair < const Key , T >>> class unordered_multiset { public : // types using key_type = Key ; using value_type = Key ; using hasher = Hash ; using key_equal = see 22.2.7 ; using key_equal = Pred ; using allocator_type = Allocator ; 
5. Possible Future Extensions
This mechanism can be extended tooperator [] 6. Implementation Experiences
Two widely used hash table implementations [SwissTables] and [F14] both enable heterogeneous operations when bothHash :: is_transparent Pred :: is_transparent key_type Hash Pred 7. Acknowledgements
We would like to thank Samuel Benzaquen, Nathan Bronson, Jay Feldblum, David Goldblatt, Chris Kennelly, Matthew Fowles Kulukundis, and Titus Winters for various discussions, insights, and implementation experiences.8. Revision History
8.1. r0 -> r1
The paper (r0) was reviewed by LEWG at the 2019 Cologne meeting with the decision of forwarding to LWG for C++20.| SF | F | N | A | SA | | 5 | 6 | 7 | 0 | 0 | 
R1 adds the §8 Revision History section and includes a typo fix in the first paragraph of §4 Proposed Wording.