From 2ca0ec0175398b245f864ef52f4ec7790577dc74 Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Tue, 29 Oct 2024 00:44:22 +0800 Subject: [PATCH 1/4] Fix and extend key extraction for unique map containers --- stl/inc/xhash | 4 +- stl/inc/xmemory | 47 ++- stl/inc/xtree | 4 +- .../test.cpp | 288 +++++++++++++++++- 4 files changed, 323 insertions(+), 20 deletions(-) diff --git a/stl/inc/xhash b/stl/inc/xhash index 0581696c1af..9017415107b 100644 --- a/stl/inc/xhash +++ b/stl/inc/xhash @@ -590,7 +590,7 @@ public: template conditional_t<_Multi, iterator, pair> emplace(_Valtys&&... _Vals) { // try to insert value_type(_Vals...) - using _In_place_key_extractor = typename _Traits::template _In_place_key_extractor<_Remove_cvref_t<_Valtys>...>; + using _In_place_key_extractor = typename _Traits::template _In_place_key_extractor<_Remove_cref_t<_Valtys>...>; if constexpr (_Multi) { _Check_max_size(); _List_node_emplace_op2<_Alnode> _Newnode(_List._Getal(), _STD forward<_Valtys>(_Vals)...); @@ -642,7 +642,7 @@ public: template iterator emplace_hint(const_iterator _Hint, _Valtys&&... _Vals) { // try to insert value_type(_Vals...) - using _In_place_key_extractor = typename _Traits::template _In_place_key_extractor<_Remove_cvref_t<_Valtys>...>; + using _In_place_key_extractor = typename _Traits::template _In_place_key_extractor<_Remove_cref_t<_Valtys>...>; if constexpr (_Multi) { _Check_max_size(); _List_node_emplace_op2<_Alnode> _Newnode(_List._Getal(), _STD forward<_Valtys>(_Vals)...); diff --git a/stl/inc/xmemory b/stl/inc/xmemory index b8e62c26196..58fea1bee02 100644 --- a/stl/inc/xmemory +++ b/stl/inc/xmemory @@ -2088,7 +2088,14 @@ _CXX17_DEPRECATE_TEMPORARY_BUFFER void return_temporary_buffer(_Ty* _Pbuf) { } #endif // _HAS_DEPRECATED_TEMPORARY_BUFFER -// assumes _Args have already been _Remove_cvref_t'd +// The key_type of an (unordered) associative container is cv-unqualified, and we can't bind const Key& to a volatile +// glvalue. +// Also, Cpp17CopyInsertable and Cpp17MoveInsertable don't require value-preservation for the construction from a +// volatile glvalue, so generally we can't perform optimization for them. See N4993 [container.alloc.reqmts]/2.3, /2.4. +template +using _Remove_cref_t = remove_const_t>; + +// assumes _Args have already been _Remove_cref_t'd template struct _In_place_key_extract_set { // by default we can't extract the key in the emplace family and must construct a node we might not use @@ -2104,7 +2111,7 @@ struct _In_place_key_extract_set<_Key, _Key> { } }; -// assumes _Args have already been _Remove_cvref_t'd +// assumes _Args have already been _Remove_cref_t'd template struct _In_place_key_extract_map { // by default we can't extract the key in the emplace family and must construct a node we might not use @@ -2123,12 +2130,44 @@ struct _In_place_key_extract_map<_Key, _Key, _Second> { template struct _In_place_key_extract_map<_Key, pair<_First, _Second>> { // if we would call the pair(pair) constructor family, we can use the pair.first member as the key - static constexpr bool _Extractable = is_same_v<_Key, _Remove_cvref_t<_First>>; - static const _Key& _Extract(const pair<_First, _Second>& _Val) { + static constexpr bool _Extractable = is_same_v<_Key, _Remove_cref_t<_First>>; + static const _Key& _Extract(const pair<_First, _Second>& _Val) noexcept { return _Val.first; } }; +#if _HAS_CXX23 +// if we would call the pair(pair-like) constructor family and the argument is not a subrange, +// we can use the get<0>(pair_like) as the key + +template +struct _In_place_key_extract_map<_Key, array<_Elem, 2>> { + static constexpr bool _Extractable = is_same_v<_Key, remove_const_t<_Elem>>; + static const _Key& _Extract(const array<_Elem, 2>& _Val) noexcept { + return _Val[0]; + } +}; + +template +struct _In_place_key_extract_map<_Key, tuple<_First, _Second>> { + static constexpr bool _Extractable = is_same_v<_Key, _Remove_cref_t<_First>>; + static const _Key& _Extract(const tuple<_First, _Second>& _Val) noexcept { + return _STD get<0>(_Val); + } +}; +#endif // _HAS_CXX23 + +template +struct _In_place_key_extract_map<_Key, piecewise_construct_t, tuple<_First>, tuple<_RestTypes...>> { + // if we would call the piecewise_construct_t constructor and the first argument is a 1-tuple, + // we can use get<0>(first_tuple) as the key + static constexpr bool _Extractable = is_same_v<_Key, _Remove_cref_t<_First>>; + static const _Key& _Extract( + const piecewise_construct_t&, const tuple<_First>& _Tup_val, const tuple<_RestTypes...>&) noexcept { + return _STD get<0>(_Tup_val); + } +}; + #pragma warning(push) #pragma warning(disable : 4624) // '%s': destructor was implicitly defined as deleted template diff --git a/stl/inc/xtree b/stl/inc/xtree index 3854fa379a3..bc6a4558719 100644 --- a/stl/inc/xtree +++ b/stl/inc/xtree @@ -998,7 +998,7 @@ private: protected: template pair<_Nodeptr, bool> _Emplace(_Valtys&&... _Vals) { - using _In_place_key_extractor = typename _Traits::template _In_place_key_extractor<_Remove_cvref_t<_Valtys>...>; + using _In_place_key_extractor = typename _Traits::template _In_place_key_extractor<_Remove_cref_t<_Valtys>...>; const auto _Scary = _Get_scary(); _Tree_find_result<_Nodeptr> _Loc; _Nodeptr _Inserted; @@ -1042,7 +1042,7 @@ public: protected: template _Nodeptr _Emplace_hint(const _Nodeptr _Hint, _Valtys&&... _Vals) { - using _In_place_key_extractor = typename _Traits::template _In_place_key_extractor<_Remove_cvref_t<_Valtys>...>; + using _In_place_key_extractor = typename _Traits::template _In_place_key_extractor<_Remove_cref_t<_Valtys>...>; const auto _Scary = _Get_scary(); _Tree_find_hint_result<_Nodeptr> _Loc; _Nodeptr _Inserted; diff --git a/tests/std/tests/VSO_0131167_associative_emplacement_allocations/test.cpp b/tests/std/tests/VSO_0131167_associative_emplacement_allocations/test.cpp index aef6e39bc2f..1cbec914707 100644 --- a/tests/std/tests/VSO_0131167_associative_emplacement_allocations/test.cpp +++ b/tests/std/tests/VSO_0131167_associative_emplacement_allocations/test.cpp @@ -1,9 +1,14 @@ // Copyright (c) Microsoft Corporation. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +#include #include #include +#include #include +#include +#include +#include #include #include #include @@ -142,23 +147,282 @@ void test_emplaces(FirstValue& val1, Values&... valn) { } } -int main() { - // This tests that unordered_(map|set) detect cases they can avoid allocating. - // It isn't strictly required by the standard, but we think it should be applicable to good implementations. +template +void test_emplace_for_unique_set() { + int lvalueInt{42}; + test_emplaces(lvalueInt); +} + +template +void test_emplace_for_unique_map() { int lvalueInt{42}; - test_emplaces, equal_to<>, TestAlloc>>(lvalueInt); - using MapType = unordered_map, equal_to<>, TestAlloc>>; - test_emplaces(lvalueInt, lvalueInt); + test_emplaces(lvalueInt, lvalueInt); + pair lvalueConstPair{42, 65}; pair lvaluePair{42, 65}; pair lvalueConstRefPair{lvalueInt, 65}; pair lvalueRefPair{lvalueInt, 65}; pair lvalueConstRefRefPair{move(lvalueInt), 65}; pair lvalueRefRefPair{move(lvalueInt), 65}; - test_emplaces(lvalueConstPair); - test_emplaces(lvaluePair); - test_emplaces(lvalueConstRefPair); - test_emplaces(lvalueRefPair); - test_emplaces(lvalueConstRefRefPair); - test_emplaces(lvalueRefRefPair); + test_emplaces(lvalueConstPair); + test_emplaces(lvaluePair); + test_emplaces(lvalueConstRefPair); + test_emplaces(lvalueRefPair); + test_emplaces(lvalueConstRefRefPair); + test_emplaces(lvalueRefRefPair); + +#if _HAS_CXX23 + tuple lvalueConstTuple{42, 65}; + tuple lvalueTuple{42, 65}; + tuple lvalueConstRefTuple{lvalueInt, 65}; + tuple lvalueRefTuple{lvalueInt, 65}; + tuple lvalueConstRefRefTuple{move(lvalueInt), 65}; + tuple lvalueRefRefTuple{move(lvalueInt), 65}; + test_emplaces(lvalueConstTuple); + test_emplaces(lvalueTuple); + test_emplaces(lvalueConstRefTuple); + test_emplaces(lvalueRefTuple); + test_emplaces(lvalueConstRefRefTuple); + test_emplaces(lvalueRefRefTuple); + + array arr{42, 65}; + array constArr{42, 65}; + test_emplaces(arr); + test_emplaces(constArr); +#endif // _HAS_CXX23 + + tuple tupleIntSixtyFive{65}; + + tuple lvalueConstOneTuple{42}; + tuple lvalueOneTuple{42}; + tuple lvalueConstRefOneTuple{lvalueInt}; + tuple lvalueRefOneTuple{lvalueInt}; + test_emplaces(piecewise_construct, lvalueConstOneTuple, tupleIntSixtyFive); + test_emplaces(piecewise_construct, lvalueOneTuple, tupleIntSixtyFive); + test_emplaces(piecewise_construct, lvalueConstRefOneTuple, tupleIntSixtyFive); + test_emplaces(piecewise_construct, lvalueRefOneTuple, tupleIntSixtyFive); +} + +// also test that the optimization strategy does not mishandle volatile arguments +template +void test_volatile_arguments_for_unique_set() { + using Key = typename SetContainer::value_type; + + SetContainer s; + + volatile Key x = 0; + const volatile Key& cx = x; + + s.emplace(x); + x = 1; + s.emplace(move(x)); + x = 2; + s.emplace(cx); + x = 3; + s.emplace(move(cx)); + + x = 4; + s.emplace_hint(s.end(), x); + x = 5; + s.emplace_hint(s.end(), move(x)); + x = 6; + s.emplace_hint(s.end(), cx); + x = 7; + s.emplace_hint(s.end(), move(cx)); + + assert((s == SetContainer{0, 1, 2, 3, 4, 5, 6, 7})); +} + +template +void test_pair_like_volatile_for_unique_map() { + using First = tuple_element_t<0, PairLike>; + using Second = tuple_element_t<1, PairLike>; + + MapContainer m; + + volatile remove_cv_t> x = 0; + { + PairLike p{static_cast(x), Second{}}; + m.emplace(p); + } + + x = 1; + { + PairLike p{static_cast(x), Second{}}; + m.emplace(move(p)); + } + + x = 2; + { + PairLike p{static_cast(x), Second{}}; + const auto& cp = p; + m.emplace(cp); + } + + x = 3; + { + PairLike p{static_cast(x), Second{}}; + const auto& cp = p; + m.emplace(move(cp)); + } + + x = 4; + { + PairLike p{static_cast(x), Second{}}; + m.emplace_hint(m.end(), p); + } + + x = 5; + { + PairLike p{static_cast(x), Second{}}; + m.emplace_hint(m.end(), move(p)); + } + + x = 6; + { + PairLike p{static_cast(x), Second{}}; + const auto& cp = p; + m.emplace_hint(m.end(), cp); + } + + x = 7; + { + PairLike p{static_cast(x), Second{}}; + const auto& cp = p; + m.emplace_hint(m.end(), move(cp)); + } + + assert((m == MapContainer{{0, {}}, {1, {}}, {2, {}}, {3, {}}, {4, {}}, {5, {}}, {6, {}}, {7, {}}})); +} + +template +void test_piecewise_volatile_for_unique_map() { + using First = tuple_element_t<0, Tuple>; + + MapContainer m; + + volatile remove_cv_t> x = 0; + { + Tuple tp{static_cast(x)}; + m.emplace(piecewise_construct, tp, tuple<>{}); + } + + x = 1; + { + Tuple tp{static_cast(x)}; + m.emplace(piecewise_construct, move(tp), tuple<>{}); + } + + x = 2; + { + Tuple tp{static_cast(x)}; + const auto& ctp = tp; + m.emplace(piecewise_construct, ctp, tuple<>{}); + } + + x = 3; + { + Tuple tp{static_cast(x)}; + const auto& ctp = tp; + m.emplace(piecewise_construct, move(ctp), tuple<>{}); + } + + x = 4; + { + Tuple tp{static_cast(x)}; + m.emplace_hint(m.end(), piecewise_construct, tp, tuple<>{}); + } + + x = 5; + { + Tuple tp{static_cast(x)}; + m.emplace_hint(m.end(), piecewise_construct, move(tp), tuple<>{}); + } + + x = 6; + { + Tuple tp{static_cast(x)}; + const auto& ctp = tp; + m.emplace_hint(m.end(), piecewise_construct, ctp, tuple<>{}); + } + + x = 7; + { + Tuple tp{static_cast(x)}; + const auto& ctp = tp; + m.emplace_hint(m.end(), piecewise_construct, move(ctp), tuple<>{}); + } + + assert((m == MapContainer{{0, {}}, {1, {}}, {2, {}}, {3, {}}, {4, {}}, {5, {}}, {6, {}}, {7, {}}})); +} + +template +void test_volatile_arguments_for_unique_map() { + using Key = typename MapContainer::key_type; + using Mapped = typename MapContainer::mapped_type; + + { + volatile Key x = 0; + const volatile Key& cx = x; + + MapContainer m; + + m.emplace(x, Mapped{}); + x = 1; + m.emplace(move(x), Mapped{}); + x = 2; + m.emplace(cx, Mapped{}); + x = 3; + m.emplace(move(cx), Mapped{}); + + x = 4; + m.emplace_hint(m.end(), x, Mapped{}); + x = 5; + m.emplace_hint(m.end(), move(x), Mapped{}); + x = 6; + m.emplace_hint(m.end(), cx, Mapped{}); + x = 7; + m.emplace_hint(m.end(), move(cx), Mapped{}); + + assert((m == MapContainer{{0, {}}, {1, {}}, {2, {}}, {3, {}}, {4, {}}, {5, {}}, {6, {}}, {7, {}}})); + } + + test_pair_like_volatile_for_unique_map>(); + test_pair_like_volatile_for_unique_map>(); + test_pair_like_volatile_for_unique_map>(); + test_pair_like_volatile_for_unique_map>(); + test_pair_like_volatile_for_unique_map>(); + test_pair_like_volatile_for_unique_map>(); +#if _HAS_CXX23 + test_pair_like_volatile_for_unique_map>(); + test_pair_like_volatile_for_unique_map>(); + test_pair_like_volatile_for_unique_map>(); + test_pair_like_volatile_for_unique_map>(); + test_pair_like_volatile_for_unique_map>(); + test_pair_like_volatile_for_unique_map>(); + + test_pair_like_volatile_for_unique_map>(); + test_pair_like_volatile_for_unique_map>(); +#endif // _HAS_CXX23 + + test_piecewise_volatile_for_unique_map>(); + test_piecewise_volatile_for_unique_map>(); + test_piecewise_volatile_for_unique_map>(); + test_piecewise_volatile_for_unique_map>(); +} + +int main() { + // This tests that unordered_(map|set) detect cases they can avoid allocating. + // It isn't strictly required by the standard, but we think it should be applicable to good implementations. + test_emplace_for_unique_set, TestAlloc>>(); + test_emplace_for_unique_set, equal_to<>, TestAlloc>>(); + + test_emplace_for_unique_map, TestAlloc>>>(); + test_emplace_for_unique_map, equal_to<>, TestAlloc>>>(); + + test_volatile_arguments_for_unique_set>(); + test_volatile_arguments_for_unique_set>(); + + test_volatile_arguments_for_unique_map>(); + test_volatile_arguments_for_unique_map>(); } From 039d89582ead89848882694829cc814a2e974685 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Tue, 29 Oct 2024 11:27:18 -0700 Subject: [PATCH 2/4] Fix grammar and rewrap comment. --- stl/inc/xmemory | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/stl/inc/xmemory b/stl/inc/xmemory index 58fea1bee02..397534d31d1 100644 --- a/stl/inc/xmemory +++ b/stl/inc/xmemory @@ -2088,10 +2088,10 @@ _CXX17_DEPRECATE_TEMPORARY_BUFFER void return_temporary_buffer(_Ty* _Pbuf) { } #endif // _HAS_DEPRECATED_TEMPORARY_BUFFER -// The key_type of an (unordered) associative container is cv-unqualified, and we can't bind const Key& to a volatile -// glvalue. -// Also, Cpp17CopyInsertable and Cpp17MoveInsertable don't require value-preservation for the construction from a -// volatile glvalue, so generally we can't perform optimization for them. See N4993 [container.alloc.reqmts]/2.3, /2.4. +// The key_type of an (unordered) associative container is cv-unqualified, and we can't bind const Key& to a +// volatile glvalue. Also, Cpp17CopyInsertable and Cpp17MoveInsertable don't require value-preservation for +// the construction from a volatile glvalue, so generally we can't perform this optimization for them. +// See N4993 [container.alloc.reqmts]/2.3, /2.4. template using _Remove_cref_t = remove_const_t>; @@ -2138,7 +2138,7 @@ struct _In_place_key_extract_map<_Key, pair<_First, _Second>> { #if _HAS_CXX23 // if we would call the pair(pair-like) constructor family and the argument is not a subrange, -// we can use the get<0>(pair_like) as the key +// we can use get<0>(pair-like) as the key template struct _In_place_key_extract_map<_Key, array<_Elem, 2>> { From 2af67500ae5654006fc132c430ef04aeb2acb8de Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Tue, 29 Oct 2024 11:41:27 -0700 Subject: [PATCH 3/4] `_Remove_cref_t` => `_Remove_const_ref_t` --- stl/inc/xhash | 6 ++++-- stl/inc/xmemory | 12 ++++++------ stl/inc/xtree | 10 ++++++---- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/stl/inc/xhash b/stl/inc/xhash index 9017415107b..46d0903ea73 100644 --- a/stl/inc/xhash +++ b/stl/inc/xhash @@ -590,7 +590,8 @@ public: template conditional_t<_Multi, iterator, pair> emplace(_Valtys&&... _Vals) { // try to insert value_type(_Vals...) - using _In_place_key_extractor = typename _Traits::template _In_place_key_extractor<_Remove_cref_t<_Valtys>...>; + using _In_place_key_extractor = + typename _Traits::template _In_place_key_extractor<_Remove_const_ref_t<_Valtys>...>; if constexpr (_Multi) { _Check_max_size(); _List_node_emplace_op2<_Alnode> _Newnode(_List._Getal(), _STD forward<_Valtys>(_Vals)...); @@ -642,7 +643,8 @@ public: template iterator emplace_hint(const_iterator _Hint, _Valtys&&... _Vals) { // try to insert value_type(_Vals...) - using _In_place_key_extractor = typename _Traits::template _In_place_key_extractor<_Remove_cref_t<_Valtys>...>; + using _In_place_key_extractor = + typename _Traits::template _In_place_key_extractor<_Remove_const_ref_t<_Valtys>...>; if constexpr (_Multi) { _Check_max_size(); _List_node_emplace_op2<_Alnode> _Newnode(_List._Getal(), _STD forward<_Valtys>(_Vals)...); diff --git a/stl/inc/xmemory b/stl/inc/xmemory index 397534d31d1..1ae0e422a9c 100644 --- a/stl/inc/xmemory +++ b/stl/inc/xmemory @@ -2093,9 +2093,9 @@ _CXX17_DEPRECATE_TEMPORARY_BUFFER void return_temporary_buffer(_Ty* _Pbuf) { // the construction from a volatile glvalue, so generally we can't perform this optimization for them. // See N4993 [container.alloc.reqmts]/2.3, /2.4. template -using _Remove_cref_t = remove_const_t>; +using _Remove_const_ref_t = remove_const_t>; -// assumes _Args have already been _Remove_cref_t'd +// assumes _Args have already been _Remove_const_ref_t'd template struct _In_place_key_extract_set { // by default we can't extract the key in the emplace family and must construct a node we might not use @@ -2111,7 +2111,7 @@ struct _In_place_key_extract_set<_Key, _Key> { } }; -// assumes _Args have already been _Remove_cref_t'd +// assumes _Args have already been _Remove_const_ref_t'd template struct _In_place_key_extract_map { // by default we can't extract the key in the emplace family and must construct a node we might not use @@ -2130,7 +2130,7 @@ struct _In_place_key_extract_map<_Key, _Key, _Second> { template struct _In_place_key_extract_map<_Key, pair<_First, _Second>> { // if we would call the pair(pair) constructor family, we can use the pair.first member as the key - static constexpr bool _Extractable = is_same_v<_Key, _Remove_cref_t<_First>>; + static constexpr bool _Extractable = is_same_v<_Key, _Remove_const_ref_t<_First>>; static const _Key& _Extract(const pair<_First, _Second>& _Val) noexcept { return _Val.first; } @@ -2150,7 +2150,7 @@ struct _In_place_key_extract_map<_Key, array<_Elem, 2>> { template struct _In_place_key_extract_map<_Key, tuple<_First, _Second>> { - static constexpr bool _Extractable = is_same_v<_Key, _Remove_cref_t<_First>>; + static constexpr bool _Extractable = is_same_v<_Key, _Remove_const_ref_t<_First>>; static const _Key& _Extract(const tuple<_First, _Second>& _Val) noexcept { return _STD get<0>(_Val); } @@ -2161,7 +2161,7 @@ template struct _In_place_key_extract_map<_Key, piecewise_construct_t, tuple<_First>, tuple<_RestTypes...>> { // if we would call the piecewise_construct_t constructor and the first argument is a 1-tuple, // we can use get<0>(first_tuple) as the key - static constexpr bool _Extractable = is_same_v<_Key, _Remove_cref_t<_First>>; + static constexpr bool _Extractable = is_same_v<_Key, _Remove_const_ref_t<_First>>; static const _Key& _Extract( const piecewise_construct_t&, const tuple<_First>& _Tup_val, const tuple<_RestTypes...>&) noexcept { return _STD get<0>(_Tup_val); diff --git a/stl/inc/xtree b/stl/inc/xtree index bc6a4558719..d74bf037f7c 100644 --- a/stl/inc/xtree +++ b/stl/inc/xtree @@ -998,8 +998,9 @@ private: protected: template pair<_Nodeptr, bool> _Emplace(_Valtys&&... _Vals) { - using _In_place_key_extractor = typename _Traits::template _In_place_key_extractor<_Remove_cref_t<_Valtys>...>; - const auto _Scary = _Get_scary(); + using _In_place_key_extractor = + typename _Traits::template _In_place_key_extractor<_Remove_const_ref_t<_Valtys>...>; + const auto _Scary = _Get_scary(); _Tree_find_result<_Nodeptr> _Loc; _Nodeptr _Inserted; if constexpr (!_Multi && _In_place_key_extractor::_Extractable) { @@ -1042,8 +1043,9 @@ public: protected: template _Nodeptr _Emplace_hint(const _Nodeptr _Hint, _Valtys&&... _Vals) { - using _In_place_key_extractor = typename _Traits::template _In_place_key_extractor<_Remove_cref_t<_Valtys>...>; - const auto _Scary = _Get_scary(); + using _In_place_key_extractor = + typename _Traits::template _In_place_key_extractor<_Remove_const_ref_t<_Valtys>...>; + const auto _Scary = _Get_scary(); _Tree_find_hint_result<_Nodeptr> _Loc; _Nodeptr _Inserted; if constexpr (!_Multi && _In_place_key_extractor::_Extractable) { From 16d24d32a432066c31b0aa0e023e8d0300853d07 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Tue, 29 Oct 2024 11:55:36 -0700 Subject: [PATCH 4/4] Centralize `_Remove_const_ref_t` with alias templates. --- stl/inc/xhash | 6 ++---- stl/inc/xmemory | 22 ++++++++++++++-------- stl/inc/xtree | 10 ++++------ 3 files changed, 20 insertions(+), 18 deletions(-) diff --git a/stl/inc/xhash b/stl/inc/xhash index 46d0903ea73..33854c4af0a 100644 --- a/stl/inc/xhash +++ b/stl/inc/xhash @@ -590,8 +590,7 @@ public: template conditional_t<_Multi, iterator, pair> emplace(_Valtys&&... _Vals) { // try to insert value_type(_Vals...) - using _In_place_key_extractor = - typename _Traits::template _In_place_key_extractor<_Remove_const_ref_t<_Valtys>...>; + using _In_place_key_extractor = typename _Traits::template _In_place_key_extractor<_Valtys...>; if constexpr (_Multi) { _Check_max_size(); _List_node_emplace_op2<_Alnode> _Newnode(_List._Getal(), _STD forward<_Valtys>(_Vals)...); @@ -643,8 +642,7 @@ public: template iterator emplace_hint(const_iterator _Hint, _Valtys&&... _Vals) { // try to insert value_type(_Vals...) - using _In_place_key_extractor = - typename _Traits::template _In_place_key_extractor<_Remove_const_ref_t<_Valtys>...>; + using _In_place_key_extractor = typename _Traits::template _In_place_key_extractor<_Valtys...>; if constexpr (_Multi) { _Check_max_size(); _List_node_emplace_op2<_Alnode> _Newnode(_List._Getal(), _STD forward<_Valtys>(_Vals)...); diff --git a/stl/inc/xmemory b/stl/inc/xmemory index 1ae0e422a9c..6060a08635f 100644 --- a/stl/inc/xmemory +++ b/stl/inc/xmemory @@ -2097,13 +2097,13 @@ using _Remove_const_ref_t = remove_const_t>; // assumes _Args have already been _Remove_const_ref_t'd template -struct _In_place_key_extract_set { +struct _In_place_key_extract_set_impl { // by default we can't extract the key in the emplace family and must construct a node we might not use static constexpr bool _Extractable = false; }; template -struct _In_place_key_extract_set<_Key, _Key> { +struct _In_place_key_extract_set_impl<_Key, _Key> { // we can extract the key in emplace if the emplaced type is identical to the key type static constexpr bool _Extractable = true; static const _Key& _Extract(const _Key& _Val) noexcept { @@ -2111,15 +2111,18 @@ struct _In_place_key_extract_set<_Key, _Key> { } }; +template +using _In_place_key_extract_set = _In_place_key_extract_set_impl<_Remove_const_ref_t<_Valtys>...>; + // assumes _Args have already been _Remove_const_ref_t'd template -struct _In_place_key_extract_map { +struct _In_place_key_extract_map_impl { // by default we can't extract the key in the emplace family and must construct a node we might not use static constexpr bool _Extractable = false; }; template -struct _In_place_key_extract_map<_Key, _Key, _Second> { +struct _In_place_key_extract_map_impl<_Key, _Key, _Second> { // if we would call the pair(key, value) constructor family, we can use the first parameter as the key static constexpr bool _Extractable = true; static const _Key& _Extract(const _Key& _Val, const _Second&) noexcept { @@ -2128,7 +2131,7 @@ struct _In_place_key_extract_map<_Key, _Key, _Second> { }; template -struct _In_place_key_extract_map<_Key, pair<_First, _Second>> { +struct _In_place_key_extract_map_impl<_Key, pair<_First, _Second>> { // if we would call the pair(pair) constructor family, we can use the pair.first member as the key static constexpr bool _Extractable = is_same_v<_Key, _Remove_const_ref_t<_First>>; static const _Key& _Extract(const pair<_First, _Second>& _Val) noexcept { @@ -2141,7 +2144,7 @@ struct _In_place_key_extract_map<_Key, pair<_First, _Second>> { // we can use get<0>(pair-like) as the key template -struct _In_place_key_extract_map<_Key, array<_Elem, 2>> { +struct _In_place_key_extract_map_impl<_Key, array<_Elem, 2>> { static constexpr bool _Extractable = is_same_v<_Key, remove_const_t<_Elem>>; static const _Key& _Extract(const array<_Elem, 2>& _Val) noexcept { return _Val[0]; @@ -2149,7 +2152,7 @@ struct _In_place_key_extract_map<_Key, array<_Elem, 2>> { }; template -struct _In_place_key_extract_map<_Key, tuple<_First, _Second>> { +struct _In_place_key_extract_map_impl<_Key, tuple<_First, _Second>> { static constexpr bool _Extractable = is_same_v<_Key, _Remove_const_ref_t<_First>>; static const _Key& _Extract(const tuple<_First, _Second>& _Val) noexcept { return _STD get<0>(_Val); @@ -2158,7 +2161,7 @@ struct _In_place_key_extract_map<_Key, tuple<_First, _Second>> { #endif // _HAS_CXX23 template -struct _In_place_key_extract_map<_Key, piecewise_construct_t, tuple<_First>, tuple<_RestTypes...>> { +struct _In_place_key_extract_map_impl<_Key, piecewise_construct_t, tuple<_First>, tuple<_RestTypes...>> { // if we would call the piecewise_construct_t constructor and the first argument is a 1-tuple, // we can use get<0>(first_tuple) as the key static constexpr bool _Extractable = is_same_v<_Key, _Remove_const_ref_t<_First>>; @@ -2168,6 +2171,9 @@ struct _In_place_key_extract_map<_Key, piecewise_construct_t, tuple<_First>, tup } }; +template +using _In_place_key_extract_map = _In_place_key_extract_map_impl<_Remove_const_ref_t<_Valtys>...>; + #pragma warning(push) #pragma warning(disable : 4624) // '%s': destructor was implicitly defined as deleted template diff --git a/stl/inc/xtree b/stl/inc/xtree index d74bf037f7c..0ec8c6fce12 100644 --- a/stl/inc/xtree +++ b/stl/inc/xtree @@ -998,9 +998,8 @@ private: protected: template pair<_Nodeptr, bool> _Emplace(_Valtys&&... _Vals) { - using _In_place_key_extractor = - typename _Traits::template _In_place_key_extractor<_Remove_const_ref_t<_Valtys>...>; - const auto _Scary = _Get_scary(); + using _In_place_key_extractor = typename _Traits::template _In_place_key_extractor<_Valtys...>; + const auto _Scary = _Get_scary(); _Tree_find_result<_Nodeptr> _Loc; _Nodeptr _Inserted; if constexpr (!_Multi && _In_place_key_extractor::_Extractable) { @@ -1043,9 +1042,8 @@ public: protected: template _Nodeptr _Emplace_hint(const _Nodeptr _Hint, _Valtys&&... _Vals) { - using _In_place_key_extractor = - typename _Traits::template _In_place_key_extractor<_Remove_const_ref_t<_Valtys>...>; - const auto _Scary = _Get_scary(); + using _In_place_key_extractor = typename _Traits::template _In_place_key_extractor<_Valtys...>; + const auto _Scary = _Get_scary(); _Tree_find_hint_result<_Nodeptr> _Loc; _Nodeptr _Inserted; if constexpr (!_Multi && _In_place_key_extractor::_Extractable) {