99#include < list>
1010#include < map>
1111#include < memory>
12+ #include < string>
13+ #include < string_view>
1214#include < tuple>
1315#include < type_traits>
1416#include < unordered_map>
1820#include < stdcorelib/stdc_global.h>
1921
2022#ifdef QT_CORE_LIB
23+ # include < QHashFunctions>
2124# include < QList>
25+ # include < QString>
26+ # include < QStringView>
2227# include < QVector>
2328#endif
2429
@@ -27,6 +32,10 @@ namespace stdc {
2732 // / \addtogroup containers
2833 // / @{
2934
35+ // / Associates an owning key type with a non-owning view type.
36+ template <class K >
37+ struct linked_map_view_traits {};
38+
3039 namespace detail {
3140
3241 template <class >
@@ -46,81 +55,162 @@ namespace stdc {
4655 struct has_capacity <T, std::void_t <decltype (std::declval<const T &>().capacity())>>
4756 : std::true_type {};
4857
49- template <class T >
50- struct linked_map_index_traits {
51- static_assert (dependent_false<T>::value,
52- " linked_map supports std::map and std::unordered_map indexes" );
53- };
58+ }
5459
55- template <class K , class V , class Compare , class Allocator >
56- struct linked_map_index_traits <std::map<K, V, Compare, Allocator>> {
57- template <class Mapped >
58- using allocator_for = typename std::allocator_traits<Allocator>::template rebind_alloc<
59- std::pair<const K, Mapped>>;
60+ // / Hashes an index key with its standard hash function.
61+ struct linked_map_hash {
62+ using is_transparent = void ;
6063
61- template <class Mapped >
62- using rebind = std::map<K, Mapped, Compare, allocator_for<Mapped>>;
64+ template <class Key >
65+ size_t operator ()(const Key &key) const noexcept (noexcept (std::hash<Key>()(key))) {
66+ return std::hash<Key>()(key);
67+ }
6368
64- template <class Mapped >
65- static rebind<Mapped> make (const Allocator &allocator) {
66- return rebind<Mapped>(Compare (), allocator_for<Mapped>(allocator));
67- }
69+ #ifdef QT_CORE_LIB
70+ size_t operator ()(QStringView key) const noexcept {
71+ return qHash (key);
72+ }
73+ #endif
74+ };
6875
69- template <class Mapped >
70- static rebind<Mapped> copy_configuration (const rebind<Mapped> &RHS ,
71- const Allocator &allocator) {
72- return rebind<Mapped>(RHS .key_comp (), allocator_for<Mapped>(allocator));
73- }
74- };
76+ // / Stores a copy of each key in the lookup index.
77+ template <class K >
78+ struct linked_map_key_traits {
79+ using key_type = K;
80+ using index_key_type = K;
7581
76- template <class K , class V , class Hash , class KeyEqual , class Allocator >
77- struct linked_map_index_traits <std::unordered_map<K, V, Hash, KeyEqual, Allocator>> {
78- template <class Mapped >
79- using allocator_for = typename std::allocator_traits<Allocator>::template rebind_alloc<
80- std::pair<const K, Mapped>>;
82+ static const index_key_type &index_key (const key_type &key) noexcept {
83+ return key;
84+ }
85+ };
8186
82- template <class Mapped >
83- using rebind = std::unordered_map<K, Mapped, Hash, KeyEqual, allocator_for<Mapped>>;
87+ namespace detail {
8488
85- template <class Mapped >
86- static rebind<Mapped> make ( const Allocator &allocator) {
87- return rebind<Mapped>( 0 , Hash (), KeyEqual (), allocator_for<Mapped>(allocator)) ;
88- }
89+ template <class K , class = void >
90+ struct linked_map_default_key_traits {
91+ using type = linked_map_key_traits<K> ;
92+ };
8993
90- template <class Mapped >
91- static rebind<Mapped> copy_configuration (const rebind<Mapped> &RHS ,
92- const Allocator &allocator) {
93- rebind<Mapped> result (RHS .bucket_count (), RHS .hash_function (), RHS .key_eq (),
94- allocator_for<Mapped>(allocator));
95- result.max_load_factor (RHS .max_load_factor ());
96- return result;
97- }
94+ template <class K >
95+ struct linked_map_default_key_traits <
96+ K, std::void_t <typename linked_map_view_traits<K>::index_key_type>> {
97+ using type = linked_map_view_traits<K>;
9898 };
9999
100100 }
101101
102+ // / Configures an unordered lookup index independently of its key representation.
103+ template <class Hash = linked_map_hash, class KeyEqual = std::equal_to<>,
104+ class Allocator = std::allocator<std::byte>>
105+ struct linked_map_unordered_traits {
106+ template <class Value >
107+ using allocator_type =
108+ typename std::allocator_traits<Allocator>::template rebind_alloc<Value>;
109+
110+ template <class Key , class Mapped , class BaseAllocator >
111+ using map_type =
112+ std::unordered_map<Key, Mapped, Hash, KeyEqual,
113+ typename std::allocator_traits<BaseAllocator>::template rebind_alloc<
114+ std::pair<const Key, Mapped>>>;
115+
116+ template <class Map , class BaseAllocator >
117+ static Map make (const BaseAllocator &allocator) {
118+ using map_allocator = typename Map::allocator_type;
119+ return Map (0 , typename Map::hasher (), typename Map::key_equal (),
120+ map_allocator (allocator));
121+ }
122+
123+ template <class Map , class BaseAllocator >
124+ static Map copy_configuration (const Map &RHS , const BaseAllocator &allocator) {
125+ using map_allocator = typename Map::allocator_type;
126+ Map result (RHS .bucket_count (), RHS .hash_function (), RHS .key_eq (),
127+ map_allocator (allocator));
128+ result.max_load_factor (RHS .max_load_factor ());
129+ return result;
130+ }
131+ };
132+
133+ // / Configures an ordered lookup index independently of its key representation.
134+ template <class Compare = std::less<>, class Allocator = std::allocator<std::byte>>
135+ struct linked_map_ordered_traits {
136+ template <class Value >
137+ using allocator_type =
138+ typename std::allocator_traits<Allocator>::template rebind_alloc<Value>;
139+
140+ template <class Key , class Mapped , class BaseAllocator >
141+ using map_type =
142+ std::map<Key, Mapped, Compare,
143+ typename std::allocator_traits<BaseAllocator>::template rebind_alloc<
144+ std::pair<const Key, Mapped>>>;
145+
146+ template <class Map , class BaseAllocator >
147+ static Map make (const BaseAllocator &allocator) {
148+ using map_allocator = typename Map::allocator_type;
149+ return Map (typename Map::key_compare (), map_allocator (allocator));
150+ }
151+
152+ template <class Map , class BaseAllocator >
153+ static Map copy_configuration (const Map &RHS , const BaseAllocator &allocator) {
154+ using map_allocator = typename Map::allocator_type;
155+ return Map (RHS .key_comp (), map_allocator (allocator));
156+ }
157+ };
158+
159+ // / Combines an index container policy with an independently reusable key representation.
160+ template <class MapTraits , class KeyTraits >
161+ struct linked_map_index_traits {
162+ using key_type = typename KeyTraits::key_type;
163+ using index_key_type = typename KeyTraits::index_key_type;
164+
165+ template <class Value >
166+ using allocator_type = typename MapTraits::template allocator_type<Value>;
167+
168+ template <class Mapped , class BaseAllocator >
169+ using map_type =
170+ typename MapTraits::template map_type<index_key_type, Mapped, BaseAllocator>;
171+
172+ static decltype (auto )
173+ index_key(const key_type &key) noexcept (noexcept (KeyTraits::index_key(key))) {
174+ return KeyTraits::index_key (key);
175+ }
176+
177+ template <class Map , class BaseAllocator >
178+ static Map make (const BaseAllocator &allocator) {
179+ return MapTraits::template make<Map>(allocator);
180+ }
181+
182+ template <class Map , class BaseAllocator >
183+ static Map copy_configuration (const Map &RHS , const BaseAllocator &allocator) {
184+ return MapTraits::template copy_configuration<Map>(RHS , allocator);
185+ }
186+ };
187+
102188 // / An associative container that preserves insertion order.
103189 // /
104- // / Values live in a list so their order and iterators remain stable. A \c std::map or
105- // / \c std::unordered_map indexes the list by key. The index stores its own copy of each key.
106- template <class K , class V , template <class , class , class ...> class Map = std::unordered_map,
107- class ... Mods>
190+ // / Values live in a list so their order and iterators remain stable. An independently
191+ // / configured associative container indexes the list. Its key traits decide whether the
192+ // / index owns another key or refers to the key in the list node.
193+ template <class K , class V , class MapTraits = linked_map_unordered_traits<>,
194+ class KeyTraits = typename detail::linked_map_default_key_traits<K>::type>
108195 class linked_map {
109196 public:
110197 using key_type = K;
111198 using mapped_type = V;
112199 using value_type = std::pair<const K, V>;
113200
114201 private:
115- using configured_map = Map<K, V, Mods...>;
116- using index_traits = detail::linked_map_index_traits<configured_map>;
117- using allocator_traits = std::allocator_traits<typename configured_map::allocator_type>;
202+ using index_traits = linked_map_index_traits<MapTraits, KeyTraits>;
203+ static_assert (std::is_same_v<K, typename index_traits::key_type>,
204+ " linked_map key and index key traits must have the same key type" );
205+
206+ using allocator_type_impl = typename index_traits::template allocator_type<value_type>;
207+ using allocator_traits = std::allocator_traits<allocator_type_impl>;
118208
119209 public:
120210 using list_allocator_type = typename allocator_traits::template rebind_alloc<value_type>;
121211 using list_type = std::list<value_type, list_allocator_type>;
122212
123- using allocator_type = typename configured_map::allocator_type ;
213+ using allocator_type = allocator_type_impl ;
124214 using iterator = typename list_type::iterator;
125215 using const_iterator = typename list_type::const_iterator;
126216 using reverse_iterator = typename list_type::reverse_iterator;
@@ -133,14 +223,14 @@ namespace stdc {
133223 using const_pointer = typename list_type::const_pointer;
134224
135225 public:
136- using map_type = typename index_traits::template rebind <iterator>;
226+ using map_type = typename index_traits::template map_type <iterator, allocator_type >;
137227
138228 linked_map () : linked_map(allocator_type()) {
139229 }
140230
141231 explicit linked_map (const allocator_type &allocator)
142232 : _list(list_allocator_type(allocator)),
143- _map(index_traits::template make<iterator >(allocator)) {
233+ _map(index_traits::template make<map_type >(allocator)) {
144234 }
145235
146236 linked_map (const linked_map &RHS )
@@ -243,7 +333,7 @@ namespace stdc {
243333 }
244334
245335 bool remove (const K &key) {
246- auto found = _map.find (key);
336+ auto found = _map.find (index_traits::index_key ( key) );
247337 if (found == _map.end ()) {
248338 return false ;
249339 }
@@ -263,29 +353,29 @@ namespace stdc {
263353
264354 iterator erase (const_iterator position) {
265355 assert (position != _list.end ());
266- auto found = _map.find (position->first );
356+ auto found = _map.find (index_traits::index_key ( position->first ) );
267357 assert (found != _map.end () && const_iterator (found->second ) == position);
268358 _map.erase (found);
269359 return _list.erase (position);
270360 }
271361
272362 iterator find (const K &key) {
273- auto found = _map.find (key);
363+ auto found = _map.find (index_traits::index_key ( key) );
274364 return found == _map.end () ? end () : found->second ;
275365 }
276366
277367 const_iterator find (const K &key) const {
278- auto found = _map.find (key);
368+ auto found = _map.find (index_traits::index_key ( key) );
279369 return found == _map.end () ? cend () : const_iterator (found->second );
280370 }
281371
282372 V value (const K &key) const {
283- auto found = _map.find (key);
373+ auto found = _map.find (index_traits::index_key ( key) );
284374 return found == _map.end () ? V () : found->second ->second ;
285375 }
286376
287377 V value (const K &key, const V &defaultValue) const {
288- auto found = _map.find (key);
378+ auto found = _map.find (index_traits::index_key ( key) );
289379 return found == _map.end () ? defaultValue : found->second ->second ;
290380 }
291381
@@ -338,7 +428,7 @@ namespace stdc {
338428 }
339429
340430 bool contains (const K &key) const {
341- return _map.find (key) != _map.end ();
431+ return _map.find (index_traits::index_key ( key) ) != _map.end ();
342432 }
343433
344434 size_type size () const noexcept {
@@ -441,7 +531,7 @@ namespace stdc {
441531
442532 linked_map (empty_copy_t , const linked_map &RHS , const allocator_type &allocator)
443533 : _list(list_allocator_type(allocator)),
444- _map (index_traits::template copy_configuration<iterator >(RHS ._map, allocator)) {
534+ _map (index_traits::template copy_configuration<map_type >(RHS ._map, allocator)) {
445535 }
446536
447537 linked_map (const linked_map &RHS , const allocator_type &allocator)
@@ -454,22 +544,26 @@ namespace stdc {
454544 template <class ... Args>
455545 std::pair<iterator, bool > emplace_impl (const_iterator position, const K &key,
456546 Args &&...args) {
457- auto indexed = _map.emplace (key, _list. end ( ));
458- if (!indexed. second ) {
459- return {indexed. first ->second , false };
547+ auto found = _map.find ( index_traits::index_key (key ));
548+ if (found != _map. end () ) {
549+ return {found ->second , false };
460550 }
461551
552+ auto inserted =
553+ _list.emplace (position, std::piecewise_construct, std::forward_as_tuple (key),
554+ std::forward_as_tuple (std::forward<Args>(args)...));
462555#ifdef STDC_HAS_EXCEPTIONS
463556 try {
464557#endif
465- auto inserted =
466- _list.emplace (position, std::piecewise_construct, std::forward_as_tuple (key),
467- std::forward_as_tuple (std::forward<Args>(args)...));
468- indexed.first ->second = inserted;
469- return {inserted, true };
558+ auto indexed = _map.emplace (index_traits::index_key (inserted->first ), inserted);
559+ if (!indexed.second ) {
560+ _list.erase (inserted);
561+ return {indexed.first ->second , false };
562+ }
563+ return {indexed.first ->second , true };
470564#ifdef STDC_HAS_EXCEPTIONS
471565 } catch (...) {
472- _map .erase (indexed. first );
566+ _list .erase (inserted );
473567 throw ;
474568 }
475569#endif
@@ -488,4 +582,24 @@ namespace stdc {
488582 // / @}
489583}
490584
585+ // / Declares the view type used by the default linked map index for the given key type.
586+ #define STDC_DECLARE_LINKED_MAP_KEY_VIEW (Key, View ) \
587+ template <> \
588+ struct stdc ::linked_map_view_traits<Key> { \
589+ using key_type = Key; \
590+ using index_key_type = View; \
591+ \
592+ static index_key_type index_key (const key_type &key) noexcept ( \
593+ std::is_nothrow_constructible_v<index_key_type, const key_type &>) { \
594+ return index_key_type (key); \
595+ } \
596+ };
597+
598+ STDC_DECLARE_LINKED_MAP_KEY_VIEW (std::string, std::string_view)
599+ STDC_DECLARE_LINKED_MAP_KEY_VIEW (std::wstring, std::wstring_view)
600+
601+ #ifdef QT_CORE_LIB
602+ STDC_DECLARE_LINKED_MAP_KEY_VIEW (QString, QStringView)
603+ #endif
604+
491605#endif // STDCORELIB_LINKED_MAP_H
0 commit comments