This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of WP status.

### 3705. Hashability shouldn't depend on `basic_string`'s allocator

Section: 23.4.6 [basic.string.hash] Status: WP Submitter: Casey Carter Opened: 2022-05-26 Last modified: 2022-07-25

Priority: Not Prioritized

View all other issues in [basic.string.hash].

View all issues with WP status.

Discussion:

23.4.6 [basic.string.hash] says:

>     template<> struct hash<string>;
>     template<> struct hash<u8string>;
>     template<> struct hash<u16string>;
>     template<> struct hash<u32string>;
>     template<> struct hash<wstring>;
>     template<> struct hash<pmr::string>;
>     template<> struct hash<pmr::u8string>;
>     template<> struct hash<pmr::u16string>;
>     template<> struct hash<pmr::u32string>;
>     template<> struct hash<pmr::wstring>;

> > -1- If `S` is one of these string types, `SV` is the corresponding string view type, and `s` is an object of type `S`, then `hash<S>()(s) == hash<SV>()(SV(s))`

Despite that the hash value of a `basic_string` object is equivalent to the hash value of a corresponding `basic_string_view` object, which has no allocator, the capability to hash a `basic_string` depends on its allocator. All of the enabled specializations have specific allocators, which fact becomes more clear if we expand the type aliases:

>     template<> struct hash<basic_string<char, char_traits<char>, allocator<char>>;
>     template<> struct hash<basic_string<char8_t, char_traits<char8_t>, allocator<char8_t>>;

>     template<> struct hash<basic_string<char16_t, char_traits<char16_t>, allocator<char16_t>>;

>     template<> struct hash<basic_string<char32_t, char_traits<char32_t>, allocator<char32_t>>;

>     template<> struct hash<basic_string<wchar_t, char_traits<wchar_t>, allocator<wchar_t>>;

>     template<> struct hash<basic_string<char, char_traits<char>, pmr::polymorphic_allocator<char>>;
>     template<> struct hash<basic_string<char8_t, char_traits<char8_t>, pmr::polymorphic_allocator<char8_t>>;

>     template<> struct hash<basic_string<char16_t, char_traits<char16_t>, pmr::polymorphic_allocator<char16_t>>;

>     template<> struct hash<basic_string<char32_t, char_traits<char32_t>, pmr::polymorphic_allocator<char32_t>>;

>     template<> struct hash<basic_string<wchar_t, char_traits<wchar_t>, pmr::polymorphic_allocator<wchar_t>>;

If the hash value doesn't depend on the allocator type, why should we care about the allocator type? I posit that we should not, and that these ten explicit specializations should be replaced by 5 partial specializations that enable hashing `basic_string` specializations using these combinations of character type and traits type with any allocator type.

[2022-06-21; Reflector poll]

Set status to Tentatively Ready after seven votes in favour during reflector poll.

[2022-07-15; LWG telecon: move to Ready]

[2022-07-25 Approved at July 2022 virtual plenary. Status changed: Ready → WP.]

Proposed resolution:

This wording is relative to N4910.

1. Modify 23.4.2 [string.syn], header `<string>` synopsis, as indicated:

   >     […]

   >     // 23.4.6 [basic.string.hash], hash support
   >     template<class T> struct hash;
   >     template<> struct hash<string>;
   >     template<> struct hash<u8string>;
   >     template<> struct hash<u16string>;
   >     template<> struct hash<u32string>;
   >     template<> struct hash<wstring>;
   >     template<> struct hash<pmr::string>;
   >     template<> struct hash<pmr::u8string>;
   >     template<> struct hash<pmr::u16string>;
   >     template<> struct hash<pmr::u32string>;
   >     template<> struct hash<pmr::wstring>;
   >     template<class A> struct hash<basic_string<char, char_traits<char>, A>>;
   >     template<class A> struct hash<basic_string<char8_t, char_traits<char8_t>, A>>;
   >     template<class A> struct hash<basic_string<char16_t, char_traits<char16_t>, A>>;
   >     template<class A> struct hash<basic_string<char32_t, char_traits<char32_t>, A>>;
   >     template<class A> struct hash<basic_string<wchar_t, char_traits<wchar_t>, A>>;

   >     […]

2. Modify 23.4.6 [basic.string.hash] as indicated:

   >     template<> struct hash<string>;
   >     template<> struct hash<u8string>;
   >     template<> struct hash<u16string>;
   >     template<> struct hash<u32string>;
   >     template<> struct hash<wstring>;
   >     template<> struct hash<pmr::string>;
   >     template<> struct hash<pmr::u8string>;
   >     template<> struct hash<pmr::u16string>;
   >     template<> struct hash<pmr::u32string>;
   >     template<> struct hash<pmr::wstring>;
   >     template<class A> struct hash<basic_string<char, char_traits<char>, A>>;
   >     template<class A> struct hash<basic_string<char8_t, char_traits<char8_t>, A>>;
   >     template<class A> struct hash<basic_string<char16_t, char_traits<char16_t>, A>>;
   >     template<class A> struct hash<basic_string<char32_t, char_traits<char32_t>, A>>;
   >     template<class A> struct hash<basic_string<wchar_t, char_traits<wchar_t>, A>>;

   > > -1- If `S` is one of these string types, `SV` is the corresponding string view type, and `s` is an object of type `S`, then `hash<S>()(s) == hash<SV>()(SV(s))`