diff --git a/stl/inc/hash_map b/stl/inc/hash_map index b204ecba06f..6675ac79037 100644 --- a/stl/inc/hash_map +++ b/stl/inc/hash_map @@ -54,9 +54,10 @@ namespace stdext { template using _In_place_key_extractor = _STD _In_place_key_extract_map<_Kty, _Args...>; - template - using _Deduce_key = const _Kty&; - using key_equal = _Tr; + + using key_equal = _Tr; + + static constexpr bool _Has_transparent_overloads = false; #if _HAS_CXX20 && defined(__EDG__) // TRANSITION, DevCom-10678753 template diff --git a/stl/inc/hash_set b/stl/inc/hash_set index 914a09c5586..4aeabd0177b 100644 --- a/stl/inc/hash_set +++ b/stl/inc/hash_set @@ -49,9 +49,10 @@ namespace stdext { template using _In_place_key_extractor = _STD _In_place_key_extract_set<_Kty, _Args...>; - template - using _Deduce_key = const _Kty&; - using key_equal = _Tr; + + using key_equal = _Tr; + + static constexpr bool _Has_transparent_overloads = false; #if _HAS_CXX20 && defined(__EDG__) // TRANSITION, DevCom-10678753 template diff --git a/stl/inc/xhash b/stl/inc/xhash index 33854c4af0a..617138b038b 100644 --- a/stl/inc/xhash +++ b/stl/inc/xhash @@ -96,8 +96,7 @@ _STD_BEGIN template struct _Uhash_choose_transparency { // transparency selector for non-transparent hashed containers - template - using _Deduce_key = const _Kty&; + static constexpr bool _Has_transparent_overloads = false; #if _HAS_CXX20 && defined(__EDG__) // TRANSITION, DevCom-10678753 template @@ -110,8 +109,7 @@ template requires _Is_transparent_v<_Hasher> && _Is_transparent_v<_Keyeq> struct _Uhash_choose_transparency<_Kty, _Hasher, _Keyeq> { // transparency selector for transparent hashed containers - template - using _Deduce_key = const _Keyty&; + static constexpr bool _Has_transparent_overloads = true; template static constexpr bool _Supports_transparency = @@ -1225,25 +1223,41 @@ private: } public: - template - _NODISCARD iterator find(typename _Traits::template _Deduce_key<_Keyty> _Keyval) { + _NODISCARD iterator find(const key_type& _Keyval) { return _List._Make_iter(_Find(_Keyval, _Traitsobj(_Keyval))); } - template - _NODISCARD const_iterator find(typename _Traits::template _Deduce_key<_Keyty> _Keyval) const { +#if _HAS_CXX20 + template + requires _Traits::_Has_transparent_overloads + _NODISCARD iterator find(const _KeyTy& _Keyval) { + return _List._Make_iter(_Find(_Keyval, _Traitsobj(_Keyval))); + } +#endif // _HAS_CXX20 + + _NODISCARD const_iterator find(const key_type& _Keyval) const { return _List._Make_const_iter(_Find(_Keyval, _Traitsobj(_Keyval))); } #if _HAS_CXX20 - template - _NODISCARD bool contains(_Traits::template _Deduce_key<_Keyty> _Keyval) const { + template + requires _Traits::_Has_transparent_overloads + _NODISCARD const_iterator find(const _KeyTy& _Keyval) const { + return _List._Make_const_iter(_Find(_Keyval, _Traitsobj(_Keyval))); + } + + _NODISCARD bool contains(const key_type& _Keyval) const { + return static_cast(_Find_last(_Keyval, _Traitsobj(_Keyval))._Duplicate); + } + + template + requires _Traits::_Has_transparent_overloads + _NODISCARD bool contains(const _KeyTy& _Keyval) const { return static_cast(_Find_last(_Keyval, _Traitsobj(_Keyval))._Duplicate); } #endif // _HAS_CXX20 - template - _NODISCARD size_type count(typename _Traits::template _Deduce_key<_Keyty> _Keyval) const { + _NODISCARD size_type count(const key_type& _Keyval) const { const size_t _Hashval = _Traitsobj(_Keyval); if constexpr (_Multi) { return _Equal_range(_Keyval, _Hashval)._Distance; @@ -1252,6 +1266,19 @@ public: } } +#if _HAS_CXX20 + template + requires _Traits::_Has_transparent_overloads + _NODISCARD size_type count(const _KeyTy& _Keyval) const { + const size_t _Hashval = _Traitsobj(_Keyval); + if constexpr (_Multi) { + return _Equal_range(_Keyval, _Hashval)._Distance; + } else { + return static_cast(_Find_last(_Keyval, _Hashval)._Duplicate); + } + } +#endif // _HAS_CXX20 + private: struct _Equal_range_result { _Unchecked_const_iterator _First; @@ -1307,18 +1334,33 @@ private: } public: - template - _NODISCARD pair equal_range(typename _Traits::template _Deduce_key<_Keyty> _Keyval) { + _NODISCARD pair equal_range(const key_type& _Keyval) { const auto _Result = _Equal_range(_Keyval, _Traitsobj(_Keyval)); return {_List._Make_iter(_Result._First._Ptr), _List._Make_iter(_Result._Last._Ptr)}; } - template - _NODISCARD pair equal_range( - typename _Traits::template _Deduce_key<_Keyty> _Keyval) const { +#if _HAS_CXX20 + template + requires _Traits::_Has_transparent_overloads + _NODISCARD pair equal_range(const _KeyTy& _Keyval) { + const auto _Result = _Equal_range(_Keyval, _Traitsobj(_Keyval)); + return {_List._Make_iter(_Result._First._Ptr), _List._Make_iter(_Result._Last._Ptr)}; + } +#endif // _HAS_CXX20 + + _NODISCARD pair equal_range(const key_type& _Keyval) const { + const auto _Result = _Equal_range(_Keyval, _Traitsobj(_Keyval)); + return {_List._Make_const_iter(_Result._First._Ptr), _List._Make_const_iter(_Result._Last._Ptr)}; + } + +#if _HAS_CXX20 + template + requires _Traits::_Has_transparent_overloads + _NODISCARD pair equal_range(const _KeyTy& _Keyval) const { const auto _Result = _Equal_range(_Keyval, _Traitsobj(_Keyval)); return {_List._Make_const_iter(_Result._First._Ptr), _List._Make_const_iter(_Result._Last._Ptr)}; } +#endif // _HAS_CXX20 void swap(_Hash& _Right) noexcept(noexcept(_Traitsobj.swap(_Right._Traitsobj))) /* strengthened */ { if (this != _STD addressof(_Right)) { diff --git a/tests/std/tests/P0919R3_heterogeneous_unordered_lookup/test.cpp b/tests/std/tests/P0919R3_heterogeneous_unordered_lookup/test.cpp index ded59d4dce7..2fc85fe256d 100644 --- a/tests/std/tests/P0919R3_heterogeneous_unordered_lookup/test.cpp +++ b/tests/std/tests/P0919R3_heterogeneous_unordered_lookup/test.cpp @@ -272,6 +272,9 @@ void assert_unique() { emplace_test_strings(cRawToExtract); #endif // _HAS_CXX23 + // GH-5207 ": Some member functions of transparent hash containers fail to work with initializer lists" + typename Container::key_type testNotInStringListSrc(testNotInString.data(), testNotInString.size()); + // Test that transparent containers pass through the string_view; non-transparent containers // are only passed in here with string_view value_type, so they also don't allocate. [[maybe_unused]] prohibit_allocations prohibitor(true); @@ -281,6 +284,18 @@ void assert_unique() { assert(c.count(testNotInString) == 0); assert_range_empty(c.equal_range(testNotInString)); + assert(c.find({testNotInStringListSrc}) == c.end()); + assert(c.contains({testNotInStringListSrc}) == false); + assert(c.count({testNotInStringListSrc}) == 0); + assert_range_empty(c.equal_range({testNotInStringListSrc})); + + // Test non-const overloads. + assert(cRaw.find(testNotInString) == cRaw.end()); + assert_range_empty(cRaw.equal_range(testNotInString)); + + assert(cRaw.find({testNotInStringListSrc}) == cRaw.end()); + assert_range_empty(cRaw.equal_range({testNotInStringListSrc})); + for (const auto& example : testStrings) { const auto target = c.find(example); assert(target != c.end()); @@ -328,6 +343,9 @@ void assert_multi() { #endif // _HAS_CXX23 } + // GH-5207 ": Some member functions of transparent hash containers fail to work with initializer lists" + typename Container::key_type testNotInStringListSrc(testNotInString.data(), testNotInString.size()); + // Test that transparent containers pass through the string_view; non-transparent containers // are only passed in here with string_view value_type, so they also don't allocate. [[maybe_unused]] prohibit_allocations prohibitor(true); @@ -337,6 +355,18 @@ void assert_multi() { assert(c.count(testNotInString) == 0); assert_range_empty(c.equal_range(testNotInString)); + assert(c.find({testNotInStringListSrc}) == c.end()); + assert(c.contains({testNotInStringListSrc}) == false); + assert(c.count({testNotInStringListSrc}) == 0); + assert_range_empty(c.equal_range({testNotInStringListSrc})); + + // Test non-const overloads. + assert(cRaw.find(testNotInString) == cRaw.end()); + assert_range_empty(cRaw.equal_range(testNotInString)); + + assert(cRaw.find({testNotInStringListSrc}) == cRaw.end()); + assert_range_empty(cRaw.equal_range({testNotInStringListSrc})); + for (const auto& example : testStrings) { const auto target = c.find(example); assert(target != c.end());