Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qmap.h
Go to the documentation of this file.
1// Copyright (C) 2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
2// Copyright (C) 2021 The Qt Company Ltd.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4// Qt-Security score:significant reason:default
5
6#ifndef QMAP_H
7#define QMAP_H
8
9#include <QtCore/qcompare.h>
10#include <QtCore/qhashfunctions.h>
11#include <QtCore/qiterator.h>
12#include <QtCore/qlist.h>
13#include <QtCore/qrefcount.h>
14#include <QtCore/qpair.h>
15#include <QtCore/qshareddata.h>
16#include <QtCore/qshareddata_impl.h>
17#include <QtCore/qttypetraits.h>
18
19#include <functional>
20#include <initializer_list>
21#include <map>
22#include <algorithm>
23
24QT_BEGIN_NAMESPACE
25
26// common code shared between QMap and QMultimap
27template <typename AMap>
28class QMapData : public QSharedData
29{
30public:
31 using Map = AMap;
32 using Key = typename Map::key_type;
33 using T = typename Map::mapped_type;
34 using value_type = typename Map::value_type;
35 using size_type = typename Map::size_type;
36 using iterator = typename Map::iterator;
37 using const_iterator = typename Map::const_iterator;
38
39 static_assert(std::is_nothrow_destructible_v<Key>, "Types with throwing destructors are not supported in Qt containers.");
40 static_assert(std::is_nothrow_destructible_v<T>, "Types with throwing destructors are not supported in Qt containers.");
41
42 Map m;
43
44 QMapData() = default;
45 explicit QMapData(const Map &other)
46 : m(other)
47 {}
48
49 explicit QMapData(Map &&other)
50 : m(std::move(other))
51 {}
52
53 // used in remove(); copies from source all the values not matching key.
54 // returns how many were NOT copied (removed).
55 size_type copyIfNotEquivalentTo(const Map &source, const Key &key)
56 {
57 Q_ASSERT(m.empty());
58
59 size_type result = 0;
60 const auto &keyCompare = source.key_comp();
61 const auto filter = [&result, &key, &keyCompare](const auto &v)
62 {
63 if (!keyCompare(key, v.first) && !keyCompare(v.first, key)) {
64 // keys are equivalent (neither a<b nor b<a) => found it
65 ++result;
66 return true;
67 }
68 return false;
69 };
70
71 std::remove_copy_if(source.cbegin(), source.cend(),
72 std::inserter(m, m.end()),
73 filter);
74 return result;
75 }
76
77 // used in key(T), count(Key, T), find(key, T), etc; returns a
78 // comparator object suitable for algorithms with std::(multi)map
79 // iterators.
80 static auto valueIsEqualTo(const T &value)
81 {
82 return [&value](const auto &v) { return v.second == value; };
83 }
84
85 Key key(const T &value, const Key &defaultKey) const
86 {
87 auto i = std::find_if(m.cbegin(),
88 m.cend(),
89 valueIsEqualTo(value));
90 if (i != m.cend())
91 return i->first;
92
93 return defaultKey;
94 }
95
96 QList<Key> keys() const
97 {
98 QList<Key> result;
99 result.reserve(m.size());
100
101 const auto extractKey = [](const auto &v) { return v.first; };
102
103 std::transform(m.cbegin(),
104 m.cend(),
105 std::back_inserter(result),
106 extractKey);
107 return result;
108 }
109
110 QList<Key> keys(const T &value) const
111 {
112 QList<Key> result;
113 result.reserve(m.size());
114 // no std::transform_if...
115 for (const auto &v : m) {
116 if (v.second == value)
117 result.append(v.first);
118 }
119 result.shrink_to_fit();
120 return result;
121 }
122
123 QList<T> values() const
124 {
125 QList<T> result;
126 result.reserve(m.size());
127
128 const auto extractValue = [](const auto &v) { return v.second; };
129
130 std::transform(m.cbegin(),
131 m.cend(),
132 std::back_inserter(result),
133 extractValue);
134 return result;
135 }
136
137 size_type count(const Key &key) const
138 {
139 return m.count(key);
140 }
141
142 // Used in erase. Allocates a new QMapData and copies, from this->m,
143 // the elements not in the [first, last) range. The return contains
144 // the new QMapData and an iterator in its map pointing at the first
145 // element after the erase.
146 struct EraseResult {
147 QMapData *data;
148 iterator it;
149 };
150
151 EraseResult erase(const_iterator first, const_iterator last) const
152 {
153 EraseResult result;
154 result.data = new QMapData;
155 result.it = result.data->m.end();
156 const auto newDataEnd = result.it;
157
158 auto i = m.begin();
159 const auto e = m.end();
160
161 // copy over all the elements before first
162 while (i != first) {
163 result.it = result.data->m.insert(newDataEnd, *i);
164 ++i;
165 }
166
167 // skip until last
168 while (i != last)
169 ++i;
170
171 // copy from last to the end
172 while (i != e) {
173 result.data->m.insert(newDataEnd, *i);
174 ++i;
175 }
176
177 if (result.it != newDataEnd)
178 ++result.it;
179
180 return result;
181 }
182};
183
184//
185// QMap
186//
187
188template <class Key, class T>
189class QMap
190{
191 using Map = std::map<Key, T>;
192 using MapData = QMapData<Map>;
193 QtPrivate::QExplicitlySharedDataPointerV2<MapData> d;
194
195 friend class QMultiMap<Key, T>;
196
197public:
198 using key_type = Key;
199 using mapped_type = T;
202
203 QMap() = default;
204
205 // implicitly generated special member functions are OK!
206
207 void swap(QMap<Key, T> &other) noexcept
208 {
209 d.swap(other.d);
210 }
211
212 QMap(std::initializer_list<std::pair<Key, T>> list)
213 {
214 for (auto &p : list)
215 insert(p.first, p.second);
216 }
217
218 explicit QMap(const std::map<Key, T> &other)
219 : d(other.empty() ? nullptr : new MapData(other))
220 {
221 }
222
223 explicit QMap(std::map<Key, T> &&other)
224 : d(other.empty() ? nullptr : new MapData(std::move(other)))
225 {
226 }
227
228 std::map<Key, T> toStdMap() const &
229 {
230 if (d)
231 return d->m;
232 return {};
233 }
234
236 {
237 if (d) {
238 if (d.isShared())
239 return d->m;
240 else
241 return std::move(d->m);
242 }
243
244 return {};
245 }
246
247#ifndef Q_QDOC
248private:
249 template <typename AKey = Key, typename AT = T,
250 QTypeTraits::compare_eq_result_container<QMap, AKey, AT> = true>
251 friend bool comparesEqual(const QMap &lhs, const QMap &rhs)
252 {
253 if (lhs.d == rhs.d)
254 return true;
255 if (!lhs.d)
256 return rhs == lhs;
257 Q_ASSERT(lhs.d);
258 return rhs.d ? (lhs.d->m == rhs.d->m) : lhs.d->m.empty();
259 }
260 QT_DECLARE_EQUALITY_OPERATORS_HELPER(QMap, QMap, /* non-constexpr */, noexcept(false),
261 template <typename AKey = Key, typename AT = T,
262 QTypeTraits::compare_eq_result_container<QMap, AKey, AT> = true>)
263 // TODO: add the other comparison operators; std::map has them.
264public:
265#else
266 friend bool operator==(const QMap &lhs, const QMap &rhs);
267 friend bool operator!=(const QMap &lhs, const QMap &rhs);
268#endif // Q_QDOC
269
270 size_type size() const { return d ? size_type(d->m.size()) : size_type(0); }
271
272 [[nodiscard]]
273 bool isEmpty() const { return d ? d->m.empty() : true; }
274
275 void detach()
276 {
277 if (d)
278 d.detach();
279 else
280 d.reset(new MapData);
281 }
282
283 bool isDetached() const noexcept
284 {
285 return d ? !d.isShared() : false; // false makes little sense, but that's shared_null's behavior...
286 }
287
288 bool isSharedWith(const QMap<Key, T> &other) const noexcept
289 {
290 return d == other.d; // also this makes little sense?
291 }
292
293 void clear()
294 {
295 if (!d)
296 return;
297
298 if (!d.isShared())
299 d->m.clear();
300 else
301 d.reset();
302 }
303
304 size_type remove(const Key &key)
305 {
306 if (!d)
307 return 0;
308
309 if (!d.isShared())
310 return size_type(d->m.erase(key));
311
312 MapData *newData = new MapData;
314
315 d.reset(newData);
316
317 return result;
318 }
319
320 template <typename Predicate>
322 {
323 return QtPrivate::associative_erase_if(*this, pred);
324 }
325
326 T take(const Key &key)
327 {
328 if (!d)
329 return T();
330
331 const auto copy = d.isShared() ? *this : QMap(); // keep `key` alive across the detach
332 // TODO: improve. There is no need of copying all the
333 // elements (the one to be removed can be skipped).
334 detach();
335
336#ifdef __cpp_lib_node_extract
337 if (const auto node = d->m.extract(key))
338 return std::move(node.mapped());
339#else
340 auto i = d->m.find(key);
341 if (i != d->m.end()) {
342 // ### breaks RVO on most compilers (but only on old-fashioned ones, so who cares?)
343 T result(std::move(i->second));
344 d->m.erase(i);
345 return result;
346 }
347#endif
348 return T();
349 }
350
351 bool contains(const Key &key) const
352 {
353 if (!d)
354 return false;
355 auto i = d->m.find(key);
356 return i != d->m.end();
357 }
358
359 Key key(const T &value, const Key &defaultKey = Key()) const
360 {
361 if (!d)
362 return defaultKey;
363
364 return d->key(value, defaultKey);
365 }
366
367 T value(const Key &key, const T &defaultValue = T()) const
368 {
369 if (!d)
370 return defaultValue;
371 const auto i = d->m.find(key);
372 if (i != d->m.cend())
373 return i->second;
374 return defaultValue;
375 }
376
377 T &operator[](const Key &key)
378 {
379 const auto copy = d.isShared() ? *this : QMap(); // keep `key` alive across the detach
380 detach();
381 auto i = d->m.find(key);
382 if (i == d->m.end())
383 i = d->m.insert({key, T()}).first;
384 return i->second;
385 }
386
387 // CHANGE: return T, not const T!
388 T operator[](const Key &key) const
389 {
390 return value(key);
391 }
392
393 QList<Key> keys() const
394 {
395 if (!d)
396 return {};
397 return d->keys();
398 }
399
400 QList<Key> keys(const T &value) const
401 {
402 if (!d)
403 return {};
404 return d->keys(value);
405 }
406
407 QList<T> values() const
408 {
409 if (!d)
410 return {};
411 return d->values();
412 }
413
414 size_type count(const Key &key) const
415 {
416 if (!d)
417 return 0;
418 return d->count(key);
419 }
420
421 size_type count() const
422 {
423 return size();
424 }
425
426 inline const Key &firstKey() const { Q_ASSERT(!isEmpty()); return constBegin().key(); }
427 inline const Key &lastKey() const { Q_ASSERT(!isEmpty()); return (--constEnd()).key(); }
428
429 inline T &first() { Q_ASSERT(!isEmpty()); return *begin(); }
430 inline const T &first() const { Q_ASSERT(!isEmpty()); return *constBegin(); }
431 inline T &last() { Q_ASSERT(!isEmpty()); return *(--end()); }
432 inline const T &last() const { Q_ASSERT(!isEmpty()); return *(--constEnd()); }
433
434 class const_iterator;
435
436 class iterator
437 {
438 friend class QMap<Key, T>;
439 friend class const_iterator;
440
441 typename Map::iterator i;
442 explicit iterator(typename Map::iterator it) : i(it) {}
443 public:
444 using iterator_category = std::bidirectional_iterator_tag;
445 using difference_type = qptrdiff;
446 using value_type = T;
447 using pointer = T *;
448 using reference = T &;
449
450 iterator() = default;
451
452 const Key &key() const { return i->first; }
453 T &value() const { return i->second; }
454 T &operator*() const { return i->second; }
455 T *operator->() const { return &i->second; }
456 friend bool operator==(const iterator &lhs, const iterator &rhs) { return lhs.i == rhs.i; }
457 friend bool operator!=(const iterator &lhs, const iterator &rhs) { return lhs.i != rhs.i; }
458
459 iterator &operator++()
460 {
461 ++i;
462 return *this;
463 }
464 iterator operator++(int)
465 {
466 iterator r = *this;
467 ++i;
468 return r;
469 }
470 iterator &operator--()
471 {
472 --i;
473 return *this;
474 }
475 iterator operator--(int)
476 {
477 iterator r = *this;
478 --i;
479 return r;
480 }
481
482#if QT_DEPRECATED_SINCE(6, 0)
483 QT_DEPRECATED_VERSION_X_6_0("Use std::next; QMap iterators are not random access")
484 //! [qmap-op-it-plus-step]
485 friend iterator operator+(iterator it, difference_type j) { return std::next(it, j); }
486
487 QT_DEPRECATED_VERSION_X_6_0("Use std::prev; QMap iterators are not random access")
488 //! [qmap-op-it-minus-step]
489 friend iterator operator-(iterator it, difference_type j) { return std::prev(it, j); }
490
491 QT_DEPRECATED_VERSION_X_6_0("Use std::next or std::advance; QMap iterators are not random access")
492 iterator &operator+=(difference_type j) { std::advance(*this, j); return *this; }
493
494 QT_DEPRECATED_VERSION_X_6_0("Use std::prev or std::advance; QMap iterators are not random access")
495 iterator &operator-=(difference_type j) { std::advance(*this, -j); return *this; }
496
497 QT_DEPRECATED_VERSION_X_6_0("Use std::next; QMap iterators are not random access")
498 //! [qmap-op-step-plus-it]
499 friend iterator operator+(difference_type j, iterator it) { return std::next(it, j); }
500
501 QT_DEPRECATED_VERSION_X_6_0("Use std::prev; QMap iterators are not random access")
502 //! [qmap-op-step-minus-it]
503 friend iterator operator-(difference_type j, iterator it) { return std::prev(it, j); }
504#endif
505 };
506
507 class const_iterator
508 {
509 friend class QMap<Key, T>;
510 typename Map::const_iterator i;
511 explicit const_iterator(typename Map::const_iterator it) : i(it) {}
512
513 public:
514 using iterator_category = std::bidirectional_iterator_tag;
515 using difference_type = qptrdiff;
516 using value_type = T;
517 using pointer = const T *;
518 using reference = const T &;
519
520 const_iterator() = default;
521 Q_IMPLICIT const_iterator(const iterator &o) : i(o.i) {}
522
523 const Key &key() const { return i->first; }
524 const T &value() const { return i->second; }
525 const T &operator*() const { return i->second; }
526 const T *operator->() const { return &i->second; }
527 friend bool operator==(const const_iterator &lhs, const const_iterator &rhs) { return lhs.i == rhs.i; }
528 friend bool operator!=(const const_iterator &lhs, const const_iterator &rhs) { return lhs.i != rhs.i; }
529
530 const_iterator &operator++()
531 {
532 ++i;
533 return *this;
534 }
535 const_iterator operator++(int)
536 {
537 const_iterator r = *this;
538 ++i;
539 return r;
540 }
541 const_iterator &operator--()
542 {
543 --i;
544 return *this;
545 }
546 const_iterator operator--(int)
547 {
548 const_iterator r = *this;
549 --i;
550 return r;
551 }
552
553#if QT_DEPRECATED_SINCE(6, 0)
554 QT_DEPRECATED_VERSION_X_6_0("Use std::next; QMap iterators are not random access")
555 //! [qmap-op-it-plus-step-const]
556 friend const_iterator operator+(const_iterator it, difference_type j) { return std::next(it, j); }
557
558 QT_DEPRECATED_VERSION_X_6_0("Use std::prev; QMap iterators are not random access")
559 //! [qmap-op-it-minus-step-const]
560 friend const_iterator operator-(const_iterator it, difference_type j) { return std::prev(it, j); }
561
562 QT_DEPRECATED_VERSION_X_6_0("Use std::next or std::advance; QMap iterators are not random access")
563 const_iterator &operator+=(difference_type j) { std::advance(*this, j); return *this; }
564
565 QT_DEPRECATED_VERSION_X_6_0("Use std::prev or std::advance; QMap iterators are not random access")
566 const_iterator &operator-=(difference_type j) { std::advance(*this, -j); return *this; }
567
568 QT_DEPRECATED_VERSION_X_6_0("Use std::next; QMap iterators are not random access")
569 //! [qmap-op-step-plus-it-const]
570 friend const_iterator operator+(difference_type j, const_iterator it) { return std::next(it, j); }
571
572 QT_DEPRECATED_VERSION_X_6_0("Use std::prev; QMap iterators are not random access")
573 //! [qmap-op-step-minus-it-const]
574 friend const_iterator operator-(difference_type j, const_iterator it) { return std::prev(it, j); }
575#endif
576 };
577
578 class key_iterator
579 {
580 const_iterator i;
581
582 public:
583 typedef typename const_iterator::iterator_category iterator_category;
584 typedef typename const_iterator::difference_type difference_type;
585 typedef Key value_type;
586 typedef const Key *pointer;
587 typedef const Key &reference;
588
589 key_iterator() = default;
590 explicit key_iterator(const_iterator o) : i(o) { }
591
592 const Key &operator*() const { return i.key(); }
593 const Key *operator->() const { return &i.key(); }
594 bool operator==(key_iterator o) const { return i == o.i; }
595 bool operator!=(key_iterator o) const { return i != o.i; }
596
597 inline key_iterator &operator++() { ++i; return *this; }
598 inline key_iterator operator++(int) { return key_iterator(i++);}
599 inline key_iterator &operator--() { --i; return *this; }
600 inline key_iterator operator--(int) { return key_iterator(i--); }
601 const_iterator base() const { return i; }
602 };
603
604 typedef QKeyValueIterator<const Key&, const T&, const_iterator> const_key_value_iterator;
605 typedef QKeyValueIterator<const Key&, T&, iterator> key_value_iterator;
606
607 // STL style
608 iterator begin() { detach(); return iterator(d->m.begin()); }
609 const_iterator begin() const { if (!d) return const_iterator(); return const_iterator(d->m.cbegin()); }
610 const_iterator constBegin() const { return begin(); }
611 const_iterator cbegin() const { return begin(); }
612 iterator end() { detach(); return iterator(d->m.end()); }
613 const_iterator end() const { if (!d) return const_iterator(); return const_iterator(d->m.end()); }
614 const_iterator constEnd() const { return end(); }
615 const_iterator cend() const { return end(); }
616 key_iterator keyBegin() const { return key_iterator(begin()); }
617 key_iterator keyEnd() const { return key_iterator(end()); }
618 key_value_iterator keyValueBegin() { return key_value_iterator(begin()); }
619 key_value_iterator keyValueEnd() { return key_value_iterator(end()); }
620 const_key_value_iterator keyValueBegin() const { return const_key_value_iterator(begin()); }
621 const_key_value_iterator constKeyValueBegin() const { return const_key_value_iterator(begin()); }
622 const_key_value_iterator keyValueEnd() const { return const_key_value_iterator(end()); }
623 const_key_value_iterator constKeyValueEnd() const { return const_key_value_iterator(end()); }
624 auto asKeyValueRange() & { return QtPrivate::QKeyValueRange<QMap &>(*this); }
625 auto asKeyValueRange() const & { return QtPrivate::QKeyValueRange<const QMap &>(*this); }
626 auto asKeyValueRange() && { return QtPrivate::QKeyValueRange<QMap>(std::move(*this)); }
627 auto asKeyValueRange() const && { return QtPrivate::QKeyValueRange<QMap>(std::move(*this)); }
628
629 iterator erase(const_iterator it)
630 {
631 return erase(it, std::next(it));
632 }
633
634 iterator erase(const_iterator afirst, const_iterator alast)
635 {
636 if (!d)
637 return iterator();
638
639 if (!d.isShared())
640 return iterator(d->m.erase(afirst.i, alast.i));
641
642 auto result = d->erase(afirst.i, alast.i);
643 d.reset(result.data);
644 return iterator(result.it);
645 }
646
647 // more Qt
648 typedef iterator Iterator;
649 typedef const_iterator ConstIterator;
650
651 iterator find(const Key &key)
652 {
653 const auto copy = d.isShared() ? *this : QMap(); // keep `key` alive across the detach
654 detach();
655 return iterator(d->m.find(key));
656 }
657
658 const_iterator find(const Key &key) const
659 {
660 if (!d)
661 return const_iterator();
662 return const_iterator(d->m.find(key));
663 }
664
665 const_iterator constFind(const Key &key) const
666 {
667 return find(key);
668 }
669
670 iterator lowerBound(const Key &key)
671 {
672 const auto copy = d.isShared() ? *this : QMap(); // keep `key` alive across the detach
673 detach();
674 return iterator(d->m.lower_bound(key));
675 }
676
677 const_iterator lowerBound(const Key &key) const
678 {
679 if (!d)
680 return const_iterator();
681 return const_iterator(d->m.lower_bound(key));
682 }
683
684 iterator upperBound(const Key &key)
685 {
686 const auto copy = d.isShared() ? *this : QMap(); // keep `key` alive across the detach
687 detach();
688 return iterator(d->m.upper_bound(key));
689 }
690
691 const_iterator upperBound(const Key &key) const
692 {
693 if (!d)
694 return const_iterator();
695 return const_iterator(d->m.upper_bound(key));
696 }
697
698 iterator insert(const Key &key, const T &value)
699 {
700 const auto copy = d.isShared() ? *this : QMap(); // keep `key` alive across the detach
701 // TODO: improve. In case of assignment, why copying first?
702 detach();
703 return iterator(d->m.insert_or_assign(key, value).first);
704 }
705
706 iterator insert(const_iterator pos, const Key &key, const T &value)
707 {
708 // TODO: improve. In case of assignment, why copying first?
709 typename Map::const_iterator dpos;
710 const auto copy = d.isShared() ? *this : QMap(); // keep `key`/`value` alive across the detach
711 if (!d || d.isShared()) {
712 auto posDistance = d ? std::distance(d->m.cbegin(), pos.i) : 0;
713 detach();
714 dpos = std::next(d->m.cbegin(), posDistance);
715 } else {
716 dpos = pos.i;
717 }
718 return iterator(d->m.insert_or_assign(dpos, key, value));
719 }
720
721 void insert(const QMap<Key, T> &map)
722 {
723 // TODO: improve. In case of assignment, why copying first?
724 if (map.isEmpty())
725 return;
726
727 detach();
728
729#ifdef __cpp_lib_node_extract
730 auto copy = map.d->m;
731 copy.merge(std::move(d->m));
732 d->m = std::move(copy);
733#else
734 // this is a std::copy, but we can't use std::inserter (need insert_or_assign...).
735 // copy in reverse order, trying to make effective use of insertionHint.
736 auto insertionHint = d->m.end();
737 auto mapIt = map.d->m.crbegin();
738 auto end = map.d->m.crend();
739 for (; mapIt != end; ++mapIt)
740 insertionHint = d->m.insert_or_assign(insertionHint, mapIt->first, mapIt->second);
741#endif
742 }
743
744 void insert(QMap<Key, T> &&map)
745 {
746 if (!map.d || map.d->m.empty())
747 return;
748
749 if (map.d.isShared()) {
750 // fall back to a regular copy
751 insert(map);
752 return;
753 }
754
755 detach();
756
757#ifdef __cpp_lib_node_extract
758 map.d->m.merge(std::move(d->m));
759 *this = std::move(map);
760#else
761 // same as above
762 auto insertionHint = d->m.end();
763 auto mapIt = map.d->m.crbegin();
764 auto end = map.d->m.crend();
765 for (; mapIt != end; ++mapIt)
766 insertionHint = d->m.insert_or_assign(insertionHint, std::move(mapIt->first), std::move(mapIt->second));
767#endif
768 }
769
770 // STL compatibility
771 [[nodiscard]]
772 inline bool empty() const
773 {
774 return isEmpty();
775 }
776
777 std::pair<iterator, iterator> equal_range(const Key &akey)
778 {
779 const auto copy = d.isShared() ? *this : QMap(); // keep `key` alive across the detach
780 detach();
781 auto result = d->m.equal_range(akey);
782 return {iterator(result.first), iterator(result.second)};
783 }
784
785 std::pair<const_iterator, const_iterator> equal_range(const Key &akey) const
786 {
787 if (!d)
788 return {};
789 auto result = d->m.equal_range(akey);
790 return {const_iterator(result.first), const_iterator(result.second)};
791 }
792
793private:
794#ifdef Q_QDOC
795 friend size_t qHash(const QMap &key, size_t seed = 0);
796#else
797# if defined(Q_CC_GHS) || defined (Q_CC_MSVC)
798 // GHS and MSVC tries to intantiate qHash() for the noexcept running into a
799 // non-SFINAE'ed hard error... Create an artificial SFINAE context as a
800 // work-around:
801 template <typename M, std::enable_if_t<std::is_same_v<M, QMap>, bool> = true>
802 friend QtPrivate::QHashMultiReturnType<typename M::key_type, typename M::mapped_type>
803# else
804 using M = QMap;
805 friend size_t
806# endif
807 qHash(const M &key, size_t seed = 0)
808 noexcept(QHashPrivate::noexceptPairHash<typename M::key_type, typename M::mapped_type>())
809 {
810 if (!key.d)
811 return seed;
812 // don't use qHashRange to avoid its compile-time overhead:
813 return std::accumulate(key.d->m.begin(), key.d->m.end(), seed,
814 QtPrivate::QHashCombine{seed});
815 }
816#endif // !Q_QDOC
817};
818
819Q_DECLARE_ASSOCIATIVE_ITERATOR(Map)
820Q_DECLARE_MUTABLE_ASSOCIATIVE_ITERATOR(Map)
821
822template <typename Key, typename T, typename Predicate>
823qsizetype erase_if(QMap<Key, T> &map, Predicate pred)
824{
825 return QtPrivate::associative_erase_if(map, pred);
826}
827
828
829//
830// QMultiMap
831//
832
833template <class Key, class T>
834class QMultiMap
835{
836 using Map = std::multimap<Key, T>;
837 using MapData = QMapData<Map>;
838 QtPrivate::QExplicitlySharedDataPointerV2<MapData> d;
839
840public:
841 using key_type = Key;
842 using mapped_type = T;
843 using difference_type = qptrdiff;
844 using size_type = qsizetype;
845
846 QMultiMap() = default;
847
848 // implicitly generated special member functions are OK!
849
850 QMultiMap(std::initializer_list<std::pair<Key,T>> list)
851 {
852 for (auto &p : list)
853 insert(p.first, p.second);
854 }
855
856 void swap(QMultiMap<Key, T> &other) noexcept
857 {
858 d.swap(other.d);
859 }
860
861 explicit QMultiMap(const QMap<Key, T> &other)
862 : d(other.isEmpty() ? nullptr : new MapData)
863 {
864 if (d) {
865 Q_ASSERT(other.d);
866 d->m.insert(other.d->m.begin(),
867 other.d->m.end());
868 }
869 }
870
871 explicit QMultiMap(QMap<Key, T> &&other)
872 : d(other.isEmpty() ? nullptr : new MapData)
873 {
874 if (d) {
875 Q_ASSERT(other.d);
876 if (other.d.isShared()) {
877 d->m.insert(other.d->m.begin(),
878 other.d->m.end());
879 } else {
880#ifdef __cpp_lib_node_extract
881 d->m.merge(std::move(other.d->m));
882#else
883 d->m.insert(std::make_move_iterator(other.d->m.begin()),
884 std::make_move_iterator(other.d->m.end()));
885#endif
886 }
887 }
888 }
889
890 explicit QMultiMap(const std::multimap<Key, T> &other)
891 : d(other.empty() ? nullptr : new MapData(other))
892 {
893 }
894
895 explicit QMultiMap(std::multimap<Key, T> &&other)
896 : d(other.empty() ? nullptr : new MapData(std::move(other)))
897 {
898 }
899
900 // CHANGE: return type
901 Q_DECL_DEPRECATED_X("Use toStdMultiMap instead")
902 std::multimap<Key, T> toStdMap() const
903 {
904 return toStdMultiMap();
905 }
906
907 std::multimap<Key, T> toStdMultiMap() const &
908 {
909 if (d)
910 return d->m;
911 return {};
912 }
913
914 std::multimap<Key, T> toStdMultiMap() &&
915 {
916 if (d) {
917 if (d.isShared())
918 return d->m;
919 else
920 return std::move(d->m);
921 }
922
923 return {};
924 }
925
926#ifndef Q_QDOC
927private:
928 template <typename AKey = Key, typename AT = T,
929 QTypeTraits::compare_eq_result_container<QMultiMap, AKey, AT> = true>
930 friend bool comparesEqual(const QMultiMap &lhs, const QMultiMap &rhs)
931 {
932 if (lhs.d == rhs.d)
933 return true;
934 if (!lhs.d)
935 return rhs == lhs;
936 Q_ASSERT(lhs.d);
937 return rhs.d ? (lhs.d->m == rhs.d->m) : lhs.d->m.empty();
938 }
939 QT_DECLARE_EQUALITY_OPERATORS_HELPER(QMultiMap, QMultiMap, /* non-constexpr */, noexcept(false),
940 template <typename AKey = Key, typename AT = T,
941 QTypeTraits::compare_eq_result_container<QMultiMap, AKey, AT> = true>)
942 // TODO: add the other comparison operators; std::multimap has them.
943public:
944#else
945 friend bool operator==(const QMultiMap &lhs, const QMultiMap &rhs);
946 friend bool operator!=(const QMultiMap &lhs, const QMultiMap &rhs);
947#endif // Q_QDOC
948
949 size_type size() const { return d ? size_type(d->m.size()) : size_type(0); }
950
951 [[nodiscard]]
952 bool isEmpty() const { return d ? d->m.empty() : true; }
953
954 void detach()
955 {
956 if (d)
957 d.detach();
958 else
959 d.reset(new MapData);
960 }
961
962 bool isDetached() const noexcept
963 {
964 return d ? !d.isShared() : false; // false makes little sense, but that's shared_null's behavior...
965 }
966
967 bool isSharedWith(const QMultiMap<Key, T> &other) const noexcept
968 {
969 return d == other.d; // also this makes little sense?
970 }
971
972 void clear()
973 {
974 if (!d)
975 return;
976
977 if (!d.isShared())
978 d->m.clear();
979 else
980 d.reset();
981 }
982
983 size_type remove(const Key &key)
984 {
985 if (!d)
986 return 0;
987
988 if (!d.isShared())
989 return size_type(d->m.erase(key));
990
991 MapData *newData = new MapData;
992 size_type result = newData->copyIfNotEquivalentTo(d->m, key);
993
994 d.reset(newData);
995
996 return result;
997 }
998
999 size_type remove(const Key &key, const T &value)
1000 {
1001 if (!d)
1002 return 0;
1003
1004 // key and value may belong to this map. As such, we need to copy
1005 // them to ensure they stay valid throughout the iteration below
1006 // (which may destroy them)
1007 const Key keyCopy = key;
1008 const T valueCopy = value;
1009
1010 // TODO: improve. Copy over only the elements not to be removed.
1011 detach();
1012
1013 size_type result = 0;
1014 const auto &keyCompare = d->m.key_comp();
1015
1016 auto i = d->m.find(keyCopy);
1017 const auto e = d->m.end();
1018
1019 while (i != e && !keyCompare(keyCopy, i->first)) {
1020 if (i->second == valueCopy) {
1021 i = d->m.erase(i);
1022 ++result;
1023 } else {
1024 ++i;
1025 }
1026 }
1027
1028 return result;
1029 }
1030
1031 template <typename Predicate>
1032 size_type removeIf(Predicate pred)
1033 {
1034 return QtPrivate::associative_erase_if(*this, pred);
1035 }
1036
1037 T take(const Key &key)
1038 {
1039 if (!d)
1040 return T();
1041
1042 const auto copy = d.isShared() ? *this : QMultiMap(); // keep `key` alive across the detach
1043
1044 // TODO: improve. There is no need of copying all the
1045 // elements (the one to be removed can be skipped).
1046 detach();
1047
1048#ifdef __cpp_lib_node_extract
1049 if (const auto node = d->m.extract(key))
1050 return std::move(node.mapped());
1051#else
1052 auto i = d->m.find(key);
1053 if (i != d->m.end()) {
1054 // ### breaks RVO on most compilers (but only on old-fashioned ones, so who cares?)
1055 T result(std::move(i->second));
1056 d->m.erase(i);
1057 return result;
1058 }
1059#endif
1060 return T();
1061 }
1062
1063 bool contains(const Key &key) const
1064 {
1065 if (!d)
1066 return false;
1067 auto i = d->m.find(key);
1068 return i != d->m.end();
1069 }
1070
1071 bool contains(const Key &key, const T &value) const
1072 {
1073 return find(key, value) != end();
1074 }
1075
1076 Key key(const T &value, const Key &defaultKey = Key()) const
1077 {
1078 if (!d)
1079 return defaultKey;
1080
1081 return d->key(value, defaultKey);
1082 }
1083
1084 T value(const Key &key, const T &defaultValue = T()) const
1085 {
1086 if (!d)
1087 return defaultValue;
1088 const auto i = d->m.find(key);
1089 if (i != d->m.cend())
1090 return i->second;
1091 return defaultValue;
1092 }
1093
1094 QList<Key> keys() const
1095 {
1096 if (!d)
1097 return {};
1098 return d->keys();
1099 }
1100
1101 QList<Key> keys(const T &value) const
1102 {
1103 if (!d)
1104 return {};
1105 return d->keys(value);
1106 }
1107
1108 QList<Key> uniqueKeys() const
1109 {
1110 QList<Key> result;
1111 if (!d)
1112 return result;
1113
1114 result.reserve(size());
1115
1116 std::unique_copy(keyBegin(), keyEnd(),
1117 std::back_inserter(result));
1118
1119 result.shrink_to_fit();
1120 return result;
1121 }
1122
1123 QList<T> values() const
1124 {
1125 if (!d)
1126 return {};
1127 return d->values();
1128 }
1129
1130 QList<T> values(const Key &key) const
1131 {
1132 QList<T> result;
1133 const auto range = equal_range(key);
1134 result.reserve(std::distance(range.first, range.second));
1135 std::copy(range.first, range.second, std::back_inserter(result));
1136 return result;
1137 }
1138
1139 size_type count(const Key &key) const
1140 {
1141 if (!d)
1142 return 0;
1143 return d->count(key);
1144 }
1145
1146 size_type count(const Key &key, const T &value) const
1147 {
1148 if (!d)
1149 return 0;
1150
1151 // TODO: improve; no need of scanning the equal_range twice.
1152 auto range = d->m.equal_range(key);
1153
1154 return size_type(std::count_if(range.first,
1155 range.second,
1156 MapData::valueIsEqualTo(value)));
1157 }
1158
1159 inline const Key &firstKey() const { Q_ASSERT(!isEmpty()); return constBegin().key(); }
1160 inline const Key &lastKey() const { Q_ASSERT(!isEmpty()); return std::next(constEnd(), -1).key(); }
1161
1162 inline T &first() { Q_ASSERT(!isEmpty()); return *begin(); }
1163 inline const T &first() const { Q_ASSERT(!isEmpty()); return *constBegin(); }
1164 inline T &last() { Q_ASSERT(!isEmpty()); return *std::next(end(), -1); }
1165 inline const T &last() const { Q_ASSERT(!isEmpty()); return *std::next(constEnd(), -1); }
1166
1167 class const_iterator;
1168
1169 class iterator
1170 {
1171 friend class QMultiMap<Key, T>;
1172 friend class const_iterator;
1173
1174 typename Map::iterator i;
1175 explicit iterator(typename Map::iterator it) : i(it) {}
1176 public:
1177 using iterator_category = std::bidirectional_iterator_tag;
1178 using difference_type = qptrdiff;
1179 using value_type = T;
1180 using pointer = T *;
1181 using reference = T &;
1182
1183 iterator() = default;
1184
1185 const Key &key() const { return i->first; }
1186 T &value() const { return i->second; }
1187 T &operator*() const { return i->second; }
1188 T *operator->() const { return &i->second; }
1189 friend bool operator==(const iterator &lhs, const iterator &rhs) { return lhs.i == rhs.i; }
1190 friend bool operator!=(const iterator &lhs, const iterator &rhs) { return lhs.i != rhs.i; }
1191
1192 iterator &operator++()
1193 {
1194 ++i;
1195 return *this;
1196 }
1197 iterator operator++(int)
1198 {
1199 iterator r = *this;
1200 ++i;
1201 return r;
1202 }
1203 iterator &operator--()
1204 {
1205 --i;
1206 return *this;
1207 }
1208 iterator operator--(int)
1209 {
1210 iterator r = *this;
1211 --i;
1212 return r;
1213 }
1214
1215#if QT_DEPRECATED_SINCE(6, 0)
1216 QT_DEPRECATED_VERSION_X_6_0("Use std::next; QMultiMap iterators are not random access")
1217 //! [qmultimap-op-it-plus-step]
1218 friend iterator operator+(iterator it, difference_type j) { return std::next(it, j); }
1219
1220 QT_DEPRECATED_VERSION_X_6_0("Use std::prev; QMultiMap iterators are not random access")
1221 //! [qmultimap-op-it-minus-step]
1222 friend iterator operator-(iterator it, difference_type j) { return std::prev(it, j); }
1223
1224 QT_DEPRECATED_VERSION_X_6_0("Use std::next or std::advance; QMultiMap iterators are not random access")
1225 iterator &operator+=(difference_type j) { std::advance(*this, j); return *this; }
1226
1227 QT_DEPRECATED_VERSION_X_6_0("Use std::prev or std::advance; QMultiMap iterators are not random access")
1228 iterator &operator-=(difference_type j) { std::advance(*this, -j); return *this; }
1229
1230 QT_DEPRECATED_VERSION_X_6_0("Use std::next; QMultiMap iterators are not random access")
1231 //! [qmultimap-op-step-plus-it]
1232 friend iterator operator+(difference_type j, iterator it) { return std::next(it, j); }
1233
1234 QT_DEPRECATED_VERSION_X_6_0("Use std::prev; QMultiMap iterators are not random access")
1235 //! [qmultimap-op-step-minus-it]
1236 friend iterator operator-(difference_type j, iterator it) { return std::prev(it, j); }
1237#endif
1238 };
1239
1240 class const_iterator
1241 {
1242 friend class QMultiMap<Key, T>;
1243 typename Map::const_iterator i;
1244 explicit const_iterator(typename Map::const_iterator it) : i(it) {}
1245
1246 public:
1247 using iterator_category = std::bidirectional_iterator_tag;
1248 using difference_type = qptrdiff;
1249 using value_type = T;
1250 using pointer = const T *;
1251 using reference = const T &;
1252
1253 const_iterator() = default;
1254 Q_IMPLICIT const_iterator(const iterator &o) : i(o.i) {}
1255
1256 const Key &key() const { return i->first; }
1257 const T &value() const { return i->second; }
1258 const T &operator*() const { return i->second; }
1259 const T *operator->() const { return &i->second; }
1260 friend bool operator==(const const_iterator &lhs, const const_iterator &rhs) { return lhs.i == rhs.i; }
1261 friend bool operator!=(const const_iterator &lhs, const const_iterator &rhs) { return lhs.i != rhs.i; }
1262
1263 const_iterator &operator++()
1264 {
1265 ++i;
1266 return *this;
1267 }
1268 const_iterator operator++(int)
1269 {
1270 const_iterator r = *this;
1271 ++i;
1272 return r;
1273 }
1274 const_iterator &operator--()
1275 {
1276 --i;
1277 return *this;
1278 }
1279 const_iterator operator--(int)
1280 {
1281 const_iterator r = *this;
1282 --i;
1283 return r;
1284 }
1285
1286#if QT_DEPRECATED_SINCE(6, 0)
1287 QT_DEPRECATED_VERSION_X_6_0("Use std::next; QMultiMap iterators are not random access")
1288 //! [qmultimap-op-it-plus-step-const]
1289 friend const_iterator operator+(const_iterator it, difference_type j) { return std::next(it, j); }
1290
1291 QT_DEPRECATED_VERSION_X_6_0("Use std::prev; QMultiMap iterators are not random access")
1292 //! [qmultimap-op-it-minus-step-const]
1293 friend const_iterator operator-(const_iterator it, difference_type j) { return std::prev(it, j); }
1294
1295 QT_DEPRECATED_VERSION_X_6_0("Use std::next or std::advance; QMultiMap iterators are not random access")
1296 const_iterator &operator+=(difference_type j) { std::advance(*this, j); return *this; }
1297
1298 QT_DEPRECATED_VERSION_X_6_0("Use std::prev or std::advance; QMultiMap iterators are not random access")
1299 const_iterator &operator-=(difference_type j) { std::advance(*this, -j); return *this; }
1300
1301 QT_DEPRECATED_VERSION_X_6_0("Use std::next; QMultiMap iterators are not random access")
1302 //! [qmultimap-op-step-plus-it-const]
1303 friend const_iterator operator+(difference_type j, const_iterator it) { return std::next(it, j); }
1304
1305 QT_DEPRECATED_VERSION_X_6_0("Use std::prev; QMultiMap iterators are not random access")
1306 //! [qmultimap-op-step-minus-it-const]
1307 friend const_iterator operator-(difference_type j, const_iterator it) { return std::prev(it, j); }
1308#endif
1309 };
1310
1311 class key_iterator
1312 {
1313 const_iterator i;
1314
1315 public:
1316 typedef typename const_iterator::iterator_category iterator_category;
1317 typedef typename const_iterator::difference_type difference_type;
1318 typedef Key value_type;
1319 typedef const Key *pointer;
1320 typedef const Key &reference;
1321
1322 key_iterator() = default;
1323 explicit key_iterator(const_iterator o) : i(o) { }
1324
1325 const Key &operator*() const { return i.key(); }
1326 const Key *operator->() const { return &i.key(); }
1327 bool operator==(key_iterator o) const { return i == o.i; }
1328 bool operator!=(key_iterator o) const { return i != o.i; }
1329
1330 inline key_iterator &operator++() { ++i; return *this; }
1331 inline key_iterator operator++(int) { return key_iterator(i++);}
1332 inline key_iterator &operator--() { --i; return *this; }
1333 inline key_iterator operator--(int) { return key_iterator(i--); }
1334 const_iterator base() const { return i; }
1335 };
1336
1337 typedef QKeyValueIterator<const Key&, const T&, const_iterator> const_key_value_iterator;
1338 typedef QKeyValueIterator<const Key&, T&, iterator> key_value_iterator;
1339
1340 // STL style
1341 iterator begin() { detach(); return iterator(d->m.begin()); }
1342 const_iterator begin() const { if (!d) return const_iterator(); return const_iterator(d->m.cbegin()); }
1343 const_iterator constBegin() const { return begin(); }
1344 const_iterator cbegin() const { return begin(); }
1345 iterator end() { detach(); return iterator(d->m.end()); }
1346 const_iterator end() const { if (!d) return const_iterator(); return const_iterator(d->m.end()); }
1347 const_iterator constEnd() const { return end(); }
1348 const_iterator cend() const { return end(); }
1349 key_iterator keyBegin() const { return key_iterator(begin()); }
1350 key_iterator keyEnd() const { return key_iterator(end()); }
1351 key_value_iterator keyValueBegin() { return key_value_iterator(begin()); }
1352 key_value_iterator keyValueEnd() { return key_value_iterator(end()); }
1353 const_key_value_iterator keyValueBegin() const { return const_key_value_iterator(begin()); }
1354 const_key_value_iterator constKeyValueBegin() const { return const_key_value_iterator(begin()); }
1355 const_key_value_iterator keyValueEnd() const { return const_key_value_iterator(end()); }
1356 const_key_value_iterator constKeyValueEnd() const { return const_key_value_iterator(end()); }
1357 auto asKeyValueRange() & { return QtPrivate::QKeyValueRange<QMultiMap &>(*this); }
1358 auto asKeyValueRange() const & { return QtPrivate::QKeyValueRange<const QMultiMap &>(*this); }
1359 auto asKeyValueRange() && { return QtPrivate::QKeyValueRange<QMultiMap>(std::move(*this)); }
1360 auto asKeyValueRange() const && { return QtPrivate::QKeyValueRange<QMultiMap>(std::move(*this)); }
1361
1362 iterator erase(const_iterator it)
1363 {
1364 return erase(it, std::next(it));
1365 }
1366
1367 iterator erase(const_iterator afirst, const_iterator alast)
1368 {
1369 if (!d)
1370 return iterator();
1371
1372 if (!d.isShared())
1373 return iterator(d->m.erase(afirst.i, alast.i));
1374
1375 auto result = d->erase(afirst.i, alast.i);
1376 d.reset(result.data);
1377 return iterator(result.it);
1378 }
1379
1380 // more Qt
1381 typedef iterator Iterator;
1382 typedef const_iterator ConstIterator;
1383
1384 size_type count() const
1385 {
1386 return size();
1387 }
1388
1389 iterator find(const Key &key)
1390 {
1391 const auto copy = d.isShared() ? *this : QMultiMap(); // keep `key` alive across the detach
1392 detach();
1393 return iterator(d->m.find(key));
1394 }
1395
1396 const_iterator find(const Key &key) const
1397 {
1398 if (!d)
1399 return const_iterator();
1400 return const_iterator(d->m.find(key));
1401 }
1402
1403 const_iterator constFind(const Key &key) const
1404 {
1405 return find(key);
1406 }
1407
1408 iterator find(const Key &key, const T &value)
1409 {
1410 const auto copy = d.isShared() ? *this : QMultiMap(); // keep `key`/`value` alive across the detach
1411
1412 detach();
1413
1414 auto range = d->m.equal_range(key);
1415 auto i = std::find_if(range.first, range.second,
1416 MapData::valueIsEqualTo(value));
1417
1418 if (i != range.second)
1419 return iterator(i);
1420 return iterator(d->m.end());
1421 }
1422
1423 const_iterator find(const Key &key, const T &value) const
1424 {
1425 if (!d)
1426 return const_iterator();
1427
1428 auto range = d->m.equal_range(key);
1429 auto i = std::find_if(range.first, range.second,
1430 MapData::valueIsEqualTo(value));
1431
1432 if (i != range.second)
1433 return const_iterator(i);
1434 return const_iterator(d->m.end());
1435 }
1436
1437 const_iterator constFind(const Key &key, const T &value) const
1438 {
1439 return find(key, value);
1440 }
1441
1442 iterator lowerBound(const Key &key)
1443 {
1444 const auto copy = d.isShared() ? *this : QMultiMap(); // keep `key` alive across the detach
1445 detach();
1446 return iterator(d->m.lower_bound(key));
1447 }
1448
1449 const_iterator lowerBound(const Key &key) const
1450 {
1451 if (!d)
1452 return const_iterator();
1453 return const_iterator(d->m.lower_bound(key));
1454 }
1455
1456 iterator upperBound(const Key &key)
1457 {
1458 const auto copy = d.isShared() ? *this : QMultiMap(); // keep `key` alive across the detach
1459 detach();
1460 return iterator(d->m.upper_bound(key));
1461 }
1462
1463 const_iterator upperBound(const Key &key) const
1464 {
1465 if (!d)
1466 return const_iterator();
1467 return const_iterator(d->m.upper_bound(key));
1468 }
1469
1470 iterator insert(const Key &key, const T &value)
1471 {
1472 const auto copy = d.isShared() ? *this : QMultiMap(); // keep `key`/`value` alive across the detach
1473 detach();
1474 // note that std::multimap inserts at the end of an equal_range for a key,
1475 // QMultiMap at the beginning.
1476 auto i = d->m.lower_bound(key);
1477 return iterator(d->m.insert(i, {key, value}));
1478 }
1479
1480 iterator insert(const_iterator pos, const Key &key, const T &value)
1481 {
1482 const auto copy = d.isShared() ? *this : QMultiMap(); // keep `key`/`value` alive across the detach
1483 typename Map::const_iterator dpos;
1484 if (!d || d.isShared()) {
1485 auto posDistance = d ? std::distance(d->m.cbegin(), pos.i) : 0;
1486 detach();
1487 dpos = std::next(d->m.cbegin(), posDistance);
1488 } else {
1489 dpos = pos.i;
1490 }
1491 return iterator(d->m.insert(dpos, {key, value}));
1492 }
1493
1494#if QT_DEPRECATED_SINCE(6, 0)
1495 QT_DEPRECATED_VERSION_X_6_0("Use insert() instead")
1496 iterator insertMulti(const Key &key, const T &value)
1497 {
1498 return insert(key, value);
1499 }
1500 QT_DEPRECATED_VERSION_X_6_0("Use insert() instead")
1501 iterator insertMulti(const_iterator pos, const Key &key, const T &value)
1502 {
1503 return insert(pos, key, value);
1504 }
1505
1506 QT_DEPRECATED_VERSION_X_6_0("Use unite() instead")
1507 void insert(const QMultiMap<Key, T> &map)
1508 {
1509 unite(map);
1510 }
1511
1512 QT_DEPRECATED_VERSION_X_6_0("Use unite() instead")
1513 void insert(QMultiMap<Key, T> &&map)
1514 {
1515 unite(std::move(map));
1516 }
1517#endif
1518
1519 iterator replace(const Key &key, const T &value)
1520 {
1521 const auto copy = d.isShared() ? *this : QMultiMap(); // keep `key`/`value` alive across the detach
1522
1523 // TODO: improve. No need of copying and then overwriting.
1524 detach();
1525
1526 // Similarly, improve here (e.g. lower_bound and hinted insert);
1527 // there's no insert_or_assign on multimaps
1528 auto i = d->m.find(key);
1529 if (i != d->m.end())
1530 i->second = value;
1531 else
1532 i = d->m.insert({key, value});
1533
1534 return iterator(i);
1535 }
1536
1537 // STL compatibility
1538 [[nodiscard]]
1539 inline bool empty() const { return isEmpty(); }
1540
1541 std::pair<iterator, iterator> equal_range(const Key &akey)
1542 {
1543 const auto copy = d.isShared() ? *this : QMultiMap(); // keep `key` alive across the detach
1544 detach();
1545 auto result = d->m.equal_range(akey);
1546 return {iterator(result.first), iterator(result.second)};
1547 }
1548
1549 std::pair<const_iterator, const_iterator> equal_range(const Key &akey) const
1550 {
1551 if (!d)
1552 return {};
1553 auto result = d->m.equal_range(akey);
1554 return {const_iterator(result.first), const_iterator(result.second)};
1555 }
1556
1557 QMultiMap &unite(const QMultiMap &other)
1558 {
1559 if (other.isEmpty())
1560 return *this;
1561
1562 detach();
1563
1564 auto copy = other.d->m;
1565#ifdef __cpp_lib_node_extract
1566 copy.merge(std::move(d->m));
1567#else
1568 copy.insert(std::make_move_iterator(d->m.begin()),
1569 std::make_move_iterator(d->m.end()));
1570#endif
1571 d->m = std::move(copy);
1572 return *this;
1573 }
1574
1575 QMultiMap &unite(QMultiMap<Key, T> &&other)
1576 {
1577 if (!other.d || other.d->m.empty())
1578 return *this;
1579
1580 if (other.d.isShared()) {
1581 // fall back to a regular copy
1582 unite(other);
1583 return *this;
1584 }
1585
1586 detach();
1587
1588#ifdef __cpp_lib_node_extract
1589 other.d->m.merge(std::move(d->m));
1590#else
1591 other.d->m.insert(std::make_move_iterator(d->m.begin()),
1592 std::make_move_iterator(d->m.end()));
1593#endif
1594 *this = std::move(other);
1595 return *this;
1596 }
1597};
1598
1599Q_DECLARE_ASSOCIATIVE_ITERATOR(MultiMap)
1600Q_DECLARE_MUTABLE_ASSOCIATIVE_ITERATOR(MultiMap)
1601
1602template <typename Key, typename T>
1603QMultiMap<Key, T> operator+(const QMultiMap<Key, T> &lhs, const QMultiMap<Key, T> &rhs)
1604{
1605 auto result = lhs;
1606 result += rhs;
1607 return result;
1608}
1609
1610template <typename Key, typename T>
1611QMultiMap<Key, T> operator+=(QMultiMap<Key, T> &lhs, const QMultiMap<Key, T> &rhs)
1612{
1613 return lhs.unite(rhs);
1614}
1615
1616template <typename Key, typename T, typename Predicate>
1617qsizetype erase_if(QMultiMap<Key, T> &map, Predicate pred)
1618{
1619 return QtPrivate::associative_erase_if(map, pred);
1620}
1621
1622QT_END_NAMESPACE
1623
1624#endif // QMAP_H
Definition qlist.h:81
Definition qmap.h:190
std::map< Key, T > toStdMap() const &
Definition qmap.h:228
QMap()=default
friend size_t qHash(const M &key, size_t seed=0) noexcept(QHashPrivate::noexceptPairHash< typename M::key_type, typename M::mapped_type >())
Definition qmap.h:807
T mapped_type
Definition qmap.h:199
QMap(std::map< Key, T > &&other)
Definition qmap.h:223
Key key_type
Definition qmap.h:198
std::map< Key, T > toStdMap() &&
Definition qmap.h:235
QMap(const std::map< Key, T > &other)
Definition qmap.h:218
friend bool comparesEqual(const QMap &lhs, const QMap &rhs)
Definition qmap.h:251
QMap(std::initializer_list< std::pair< Key, T > > list)
Definition qmap.h:212
void swap(QMap< Key, T > &other) noexcept
Definition qmap.h:207
void sync() override
~QWinSettingsPrivate() override
void clear() override
bool isWritable() const override
void set(const QString &uKey, const QVariant &value) override
void remove(const QString &uKey) override
QStringList children(const QString &uKey, ChildSpec spec) const override
std::optional< QVariant > get(const QString &uKey) const override
std::optional< QVariant > readKey(HKEY parentHandle, const QString &rSubKey) const
void flush() override
QWinSettingsPrivate(QString rKey, REGSAM access=0)
QString fileName() const override
HKEY handle() const
bool readOnly() const
HKEY parentHandle() const
RegistryKey(HKEY parent_handle=0, const QString &key=QString(), bool read_only=true, REGSAM access=0)
QString key() const
Combined button and popup list for selecting options.
qsizetype erase_if(QMultiHash< Key, T > &hash, Predicate pred)
Definition qhash.h:2783
QMultiMap< Key, T > operator+=(QMultiMap< Key, T > &lhs, const QMultiMap< Key, T > &rhs)
Definition qmap.h:1611
static void deleteChildGroups(HKEY parentHandle, REGSAM access=0)
static QString escapedKey(QString uKey)
QList< RegistryKey > RegistryKeyList
static void mergeKeySets(NameSet *dest, const NameSet &src)
static QString unescapedKey(QString rKey)
static void allKeys(HKEY parentHandle, const QString &rSubKey, NameSet *result, REGSAM access=0)
static HKEY createOrOpenKey(HKEY parentHandle, REGSAM perms, const QString &rSubKey, REGSAM access=0)
static QString keyPath(const QString &rKey)
static QString keyName(const QString &rKey)
#define KEY_WOW64_32KEY
QMap< QString, QString > NameSet
static QStringList childKeysOrGroups(HKEY parentHandle, QSettingsPrivate::ChildSpec spec)
static HKEY openKey(HKEY parentHandle, REGSAM perms, const QString &rSubKey, REGSAM access=0)
#define KEY_WOW64_64KEY
static const REGSAM registryPermissions
static HKEY createOrOpenKey(HKEY parentHandle, const QString &rSubKey, bool *readOnly, REGSAM access=0)