Revision control

++ // Copyright (C) 2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
++ // Copyright (C) 2021 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
++ // Qt-Security score:significant reason:default
++
++ #ifndef QMAP_H
++ #define QMAP_H
++
++ #include <QtCore/qcompare.h>
++ #include <QtCore/qhashfunctions.h>
++ #include <QtCore/qiterator.h>
++ #include <QtCore/qlist.h>
++ #include <QtCore/qrefcount.h>
++ #include <QtCore/qpair.h>
++ #include <QtCore/qscopeguard.h>
++ #include <QtCore/qshareddata.h>
++ #include <QtCore/qshareddata_impl.h>
++ #include <QtCore/qttypetraits.h>
++
++ #include <functional>
++ #include <initializer_list>
++ #include <map>
++ #include <algorithm>
++
++ QT_BEGIN_NAMESPACE
++
++ // common code shared between QMap and QMultimap
++ template <typename AMap>
++ class QMapData : public QSharedData
++ {
++ public:
++ using Map = AMap;
++ using Key = typename Map::key_type;
++ using T = typename Map::mapped_type;
++ using value_type = typename Map::value_type;
++ using size_type = typename Map::size_type;
++ using iterator = typename Map::iterator;
++ using const_iterator = typename Map::const_iterator;
++
++ static_assert(std::is_nothrow_destructible_v<Key>, "Types with throwing destructors are not supported in Qt containers.");
++ static_assert(std::is_nothrow_destructible_v<T>, "Types with throwing destructors are not supported in Qt containers.");
++
++ Map m;
++
++ QMapData() = default;
++ explicit QMapData(const Map &other)
++ : m(other)
++ {}
++
++ explicit QMapData(Map &&other)
++ : m(std::move(other))
++ {}
++
++ // copies from source all the values not matching key.
++ // returns how many were NOT copied (removed), and first removed iterator
++ auto copyIfNotEquivalentTo(const Map &source, const Key &key)
++ {
++ Q_ASSERT(m.empty());
++
++ size_type result = 0;
++ auto foundIt = source.end();
++
++ const auto markFound = [&](auto it) {
++ if (result == 0)
++ foundIt = it;
++ ++result;
++ };
++ const auto keep = [this](auto it) { m.insert(m.cend(), *it); };
++
++ auto it = source.cbegin();
++ const auto end = source.cend();
++ const auto &cmp = m.key_comp();
++ // Keep all before:
++ for (; it != end && cmp(it->first, key); ++it)
++ keep(it);
++ // Count and skip matches:
++ for (; it != end && !cmp(key, it->first); ++it)
++ markFound(it);
++ // Keep all after:
++ for (; it != end; ++it)
++ keep(it);
++
++ struct resultType { size_type count; decltype(source.begin()) iterator; };
++ return resultType{result, foundIt};
++ }
++
++ void copyExceptFor(const Map &source, const iterator &skipit)
++ {
++ Q_ASSERT(m.empty());
++
++ auto it = source.cend();
++ const auto end = source.cbegin();
++ auto hint = m.end();
++ if (it == end)
++ return;
++ do {
++ --it;
++ if (it == skipit)
++ continue;
++ hint = m.emplace_hint(hint, it->first, it->second);
++ } while (it != end);
++ }
++
++ // Merges the two sources into this one, giving preference to source2
++ void fillWithMergeOf(const Map &source1, const Map &source2)
++ {
++ Q_ASSERT(m.empty());
++
++ auto insertionHint = m.end();
++ auto src1It = source1.crbegin();
++ const auto src1End = source1.crend();
++ auto src2It = source2.crbegin();
++ const auto src2End = source2.crend();
++ const auto &keyCompare = m.key_comp();
++ while (src1It != src1End && src2It != src2End) {
++ if (keyCompare(src2It->first, src1It->first)) {
++ insertionHint = m.emplace_hint(insertionHint, src1It->first, src1It->second);
++ ++src1It;
++ } else if (keyCompare(src1It->first, src2It->first)) {
++ insertionHint = m.emplace_hint(insertionHint, src2It->first, src2It->second);
++ ++src2It;
++ } else {
++ // Equivalence, insert source2, forget source1
++ insertionHint = m.emplace_hint(insertionHint, src2It->first, src2It->second);
++ ++src1It;
++ ++src2It;
++ }
++ }
++ for (; src1It != src1End; ++src1It)
++ insertionHint = m.emplace_hint(insertionHint, src1It->first, src1It->second);
++ for (; src2It != src2End; ++src2It)
++ insertionHint = m.emplace_hint(insertionHint, src2It->first, src2It->second);
++ }
++
++ // Merge source into this one without changing source as std::map::merge would
++ void insertMap(const Map &source)
++ {
++ Q_ASSERT(!m.empty());
++ // copy in reverse order, trying to make effective use of insertionHint.
++ auto insertionHint = m.end();
++ auto it = source.crbegin();
++ const auto end = source.crend();
++ for (; it != end; ++it)
++ insertionHint = m.emplace_hint(insertionHint, it->first, it->second);
++ }
++
++ // used in key(T), count(Key, T), find(key, T), etc; returns a
++ // comparator object suitable for algorithms with std::(multi)map
++ // iterators.
++ static auto valueIsEqualTo(const T &value)
++ {
++ return [&value](const auto &v) { return v.second == value; };
++ }
++
++ Key key(const T &value, const Key &defaultKey) const
++ {
++ auto i = std::find_if(m.cbegin(),
++ m.cend(),
++ valueIsEqualTo(value));
++ if (i != m.cend())
++ return i->first;
++
++ return defaultKey;
++ }
++
++ QList<Key> keys() const
++ {
++ QList<Key> result;
++ result.reserve(m.size());
++
++ const auto extractKey = [](const auto &v) { return v.first; };
++
++ std::transform(m.cbegin(),
++ m.cend(),
++ std::back_inserter(result),
++ extractKey);
++ return result;
++ }
++
++ QList<Key> keys(const T &value) const
++ {
++ QList<Key> result;
++ result.reserve(m.size());
++ // no std::transform_if...
++ for (const auto &v : m) {
++ if (v.second == value)
++ result.append(v.first);
++ }
++ result.shrink_to_fit();
++ return result;
++ }
++
++ QList<T> values() const
++ {
++ QList<T> result;
++ result.reserve(m.size());
++
++ const auto extractValue = [](const auto &v) { return v.second; };
++
++ std::transform(m.cbegin(),
++ m.cend(),
++ std::back_inserter(result),
++ extractValue);
++ return result;
++ }
++
++ size_type count(const Key &key) const
++ {
++ return m.count(key);
++ }
++
++ // Used in erase. Allocates a new QMapData and copies, from this->m,
++ // the elements not in the [first, last) range. The return contains
++ // the new QMapData and an iterator in its map pointing at the first
++ // element after the erase.
++ struct EraseResult {
++ QMapData *data;
++ iterator it;
++ };
++
++ EraseResult erase(const_iterator first, const_iterator last) const
++ {
++ EraseResult result;
++ result.data = new QMapData;
++ result.it = result.data->m.end();
++ const auto newDataEnd = result.it;
++
++ auto i = m.begin();
++ const auto e = m.end();
++
++ // copy over all the elements before first
++ while (i != first) {
++ result.it = result.data->m.insert(newDataEnd, *i);
++ ++i;
++ }
++
++ // skip until last
++ while (i != last)
++ ++i;
++
++ // copy from last to the end
++ while (i != e) {
++ result.data->m.insert(newDataEnd, *i);
++ ++i;
++ }
++
++ if (result.it != newDataEnd)
++ ++result.it;
++
++ return result;
++ }
++ };
++
++ // common type traits
++ namespace QtPrivate {
++
++ template <typename Container, typename ...Ts>
++ using map_has_operator_less_than =
++ std::conjunction<QTypeTraits::has_operator_less_than_container<Container, Ts>...>;
++
++ // The is_base_of<Container, T> check is required for recursive containers.
++ // Without it MSVC produces errors like:
++ //
++ // error C2968:
++ // 'if_map_has_relational_operators<QMap<NoCmpParamRecursiveMapK, Empty>,
++ // NoCmpParamRecursiveMapK, Empty>':
++ // recursive alias declaration
++ //
++ // The solution is similar to QTypeTraits::*_container checks.
++ template <typename Container, typename T>
++ using map_has_qt_compare_three_way_container =
++ std::disjunction<std::is_base_of<Container, T>, Qt::has_qt_compare_three_way<T>>;
++
++ template <typename Container, typename ...Ts>
++ using map_has_qt_compare_three_way =
++ std::conjunction<map_has_qt_compare_three_way_container<Container, Ts>...>;
++
++ template <typename Container, typename ...Ts>
++ using if_map_has_relational_operators =
++ std::enable_if_t<
++ std::disjunction_v<
++ map_has_operator_less_than<Container, Ts...>,
++ map_has_qt_compare_three_way<Container, Ts...>
++ >,
++ bool>;
++
++ } // namespace QtPrivate
++
++ //
++ // QMap
++ //
++
++ template <class Key, class T>
++ class QMap
++ {
++ using Map = std::map<Key, T>;
++ using MapData = QMapData<Map>;
++ QtPrivate::QExplicitlySharedDataPointerV2<MapData> d;
++
++ friend class QMultiMap<Key, T>;
++
++ public:
++ using key_type = Key;
++ using mapped_type = T;
++ using difference_type = qptrdiff;
++ using size_type = qsizetype;
++
++ QMap() = default;
++
++ // implicitly generated special member functions are OK!
++
++ void swap(QMap<Key, T> &other) noexcept
++ {
++ d.swap(other.d);
++ }
++
++ QMap(std::initializer_list<std::pair<Key, T>> list)
++ {
++ for (auto &p : list)
++ insert(p.first, p.second);
++ }
++
++ explicit QMap(const std::map<Key, T> &other)
++ : d(other.empty() ? nullptr : new MapData(other))
++ {
++ }
++
++ explicit QMap(std::map<Key, T> &&other)
++ : d(other.empty() ? nullptr : new MapData(std::move(other)))
++ {
++ }
++
++ std::map<Key, T> toStdMap() const &
++ {
++ if (d)
++ return d->m;
++ return {};
++ }
++
++ std::map<Key, T> toStdMap() &&
++ {
++ if (d) {
++ if (d.isShared())
++ return d->m;
++ else
++ return std::move(d->m);
++ }
++
++ return {};
++ }
++
++ #ifndef Q_QDOC
++ private:
++ template <typename AKey = Key, typename AT = T,
++ QTypeTraits::compare_eq_result_container<QMap, AKey, AT> = true>
++ friend bool comparesEqual(const QMap &lhs, const QMap &rhs)
++ {
++ if (lhs.d == rhs.d)
++ return true;
++ if (!lhs.d)
++ return rhs == lhs;
++ Q_ASSERT(lhs.d);
++ return rhs.d ? (lhs.d->m == rhs.d->m) : lhs.d->m.empty();
++ }
++ QT_DECLARE_EQUALITY_OPERATORS_HELPER(QMap, QMap, /* non-constexpr */, noexcept(false),
++ template <typename AKey = Key, typename AT = T,
++ QTypeTraits::compare_eq_result_container<QMap, AKey, AT> = true>)
++
++ template <typename AKey = Key, typename AT = T,
++ QtPrivate::if_map_has_relational_operators<QMap, AKey, AT> = true>
++ friend auto compareThreeWay(const QMap &lhs, const QMap &rhs)
++ {
++ return QtOrderingPrivate::lexicographicalCompareThreeWay(lhs.constKeyValueBegin(),
++ lhs.constKeyValueEnd(),
++ rhs.constKeyValueBegin(),
++ rhs.constKeyValueEnd());
++ }
++ QT_DECLARE_ORDERING_HELPER_AUTO(QMap, QMap, /* non-constexpr */, noexcept(false),
++ template <typename AKey = Key, typename AT = T,
++ QtPrivate::if_map_has_relational_operators<QMap, AKey, AT> = true>)
++
++ public:
++ #else
++ friend bool operator==(const QMap &lhs, const QMap &rhs);
++ friend bool operator!=(const QMap &lhs, const QMap &rhs);
++ friend bool operator<(const QMap &lhs, const QMap &rhs);
++ friend bool operator>(const QMap &lhs, const QMap &rhs);
++ friend bool operator<=(const QMap &lhs, const QMap &rhs);
++ friend bool operator>=(const QMap &lhs, const QMap &rhs);
++ friend auto operator<=>(const QMap &lhs, const QMap &rhs);
++ #endif // Q_QDOC
++
++ size_type size() const { return d ? size_type(d->m.size()) : size_type(0); }
++
++ [[nodiscard]]
++ bool isEmpty() const { return d ? d->m.empty() : true; }
++
++ void detach()
++ {
++ if (d)
++ d.detach();
++ else
++ d.reset(new MapData);
++ }
++
++ // A detach for holding an already shared copy, until calling function
++ // is done using references to keys or values that might reference it.
++ [[nodiscard]]
++ QMap referenceHoldingDetach()
++ {
++ if (!d) {
++ d.reset(new MapData);
++ } else if (d.isShared()) {
++ auto hold = *this;
++ d.detach();
++ return hold;
++ }
++ return {};
++ }
++
++ // Specialized version of referenceHoldingDetach(), which will not copy key, if copying
++ [[nodiscard]]
++ QMap referenceHoldingDetachExcept(const Key &key)
++ {
++ if (!d) {
++ d.reset(new MapData);
++ } else if (d.isShared()) {
++ auto hold = *this;
++ QtPrivate::QExplicitlySharedDataPointerV2<MapData> newData(new MapData);
++ newData->copyIfNotEquivalentTo(d->m, key);
++ d.swap(newData);
++ return hold;
++ }
++ return {};
++ }
++
++ bool isDetached() const noexcept
++ {
++ return d ? !d.isShared() : false; // false makes little sense, but that's shared_null's behavior...
++ }
++
++ bool isSharedWith(const QMap<Key, T> &other) const noexcept
++ {
++ return d == other.d; // also this makes little sense?
++ }
++
++ void clear()
++ {
++ if (!d)
++ return;
++
++ if (!d.isShared())
++ d->m.clear();
++ else
++ d.reset();
++ }
++
++ size_type remove(const Key &key)
++ {
++ if (!d)
++ return 0;
++
++ if (!d.isShared())
++ return size_type(d->m.erase(key));
++
++ MapData *newData = new MapData;
++ size_type result = newData->copyIfNotEquivalentTo(d->m, key).count;
++
++ d.reset(newData);
++
++ return result;
++ }
++
++ template <typename Predicate>
++ size_type removeIf(Predicate pred)
++ {
++ return QtPrivate::associative_erase_if(*this, pred);
++ }
++
++ T take(const Key &key)
++ {
++ if (!d)
++ return T();
++
++ if (d.isShared()) {
++ MapData *m = new MapData;
++ // For historic reasons, we always un-share (was: detach()) when
++ // this function is called, even if `key` isn't found
++ const auto commit = qScopeGuard([&] { d.reset(m); });
++
++ auto result = m->copyIfNotEquivalentTo(d->m, key);
++ if (result.count)
++ return result.iterator->second;
++ // if we reach here, `key` wasn't found:
++ return T();
++ }
++
++ #ifdef __cpp_lib_node_extract
++ if (const auto node = d->m.extract(key))
++ return std::move(node.mapped());
++ #else
++ auto i = d->m.find(key);
++ if (i != d->m.end()) {
++ // ### breaks RVO on most compilers (but only on old-fashioned ones, so who cares?)
++ T result(std::move(i->second));
++ d->m.erase(i);
++ return result;
++ }
++ #endif
++ return T();
++ }
++
++ bool contains(const Key &key) const
++ {
++ if (!d)
++ return false;
++ auto i = d->m.find(key);
++ return i != d->m.end();
++ }
++
++ Key key(const T &value, const Key &defaultKey = Key()) const
++ {
++ if (!d)
++ return defaultKey;
++
++ return d->key(value, defaultKey);
++ }
++
++ T value(const Key &key, const T &defaultValue = T()) const
++ {
++ if (!d)
++ return defaultValue;
++ const auto i = d->m.find(key);
++ if (i != d->m.cend())
++ return i->second;
++ return defaultValue;
++ }
++
++ T &operator[](const Key &key)
++ {
++ const auto hold = referenceHoldingDetach();
++ auto i = d->m.find(key);
++ if (i == d->m.end())
++ i = d->m.insert({key, T()}).first;
++ return i->second;
++ }
++
++ // CHANGE: return T, not const T!
++ T operator[](const Key &key) const
++ {
++ return value(key);
++ }
++
++ QList<Key> keys() const
++ {
++ if (!d)
++ return {};
++ return d->keys();
++ }
++
++ QList<Key> keys(const T &value) const
++ {
++ if (!d)
++ return {};
++ return d->keys(value);
++ }
++
++ QList<T> values() const
++ {
++ if (!d)
++ return {};
++ return d->values();
++ }
++
++ size_type count(const Key &key) const
++ {
++ if (!d)
++ return 0;
++ return d->count(key);
++ }
++
++ size_type count() const
++ {
++ return size();
++ }
++
++ inline const Key &firstKey() const { Q_ASSERT(!isEmpty()); return constBegin().key(); }
++ inline const Key &lastKey() const { Q_ASSERT(!isEmpty()); return (--constEnd()).key(); }
++
++ inline T &first() { Q_ASSERT(!isEmpty()); return *begin(); }
++ inline const T &first() const { Q_ASSERT(!isEmpty()); return *constBegin(); }
++ inline T &last() { Q_ASSERT(!isEmpty()); return *(--end()); }
++ inline const T &last() const { Q_ASSERT(!isEmpty()); return *(--constEnd()); }
++
++ class const_iterator;
++
++ class iterator
++ {
++ friend class QMap<Key, T>;
++ friend class const_iterator;
++
++ typename Map::iterator i;
++ explicit iterator(typename Map::iterator it) : i(it) {}
++ public:
++ using iterator_category = std::bidirectional_iterator_tag;
++ using difference_type = qptrdiff;
++ using value_type = T;
++ using pointer = T *;
++ using reference = T &;
++
++ iterator() = default;
++
++ const Key &key() const { return i->first; }
++ T &value() const { return i->second; }
++ T &operator*() const { return i->second; }
++ T *operator->() const { return &i->second; }
++ friend bool operator==(const iterator &lhs, const iterator &rhs) { return lhs.i == rhs.i; }
++ friend bool operator!=(const iterator &lhs, const iterator &rhs) { return lhs.i != rhs.i; }
++
++ iterator &operator++()
++ {
++ ++i;
++ return *this;
++ }
++ iterator operator++(int)
++ {
++ iterator r = *this;
++ ++i;
++ return r;
++ }
++ iterator &operator--()
++ {
++ --i;
++ return *this;
++ }
++ iterator operator--(int)
++ {
++ iterator r = *this;
++ --i;
++ return r;
++ }
++
++ #if QT_DEPRECATED_SINCE(6, 0)
++ QT_DEPRECATED_VERSION_X_6_0("Use std::next; QMap iterators are not random access")
++ //! [qmap-op-it-plus-step]
++ friend iterator operator+(iterator it, difference_type j) { return std::next(it, j); }
++
++ QT_DEPRECATED_VERSION_X_6_0("Use std::prev; QMap iterators are not random access")
++ //! [qmap-op-it-minus-step]
++ friend iterator operator-(iterator it, difference_type j) { return std::prev(it, j); }
++
++ QT_DEPRECATED_VERSION_X_6_0("Use std::next or std::advance; QMap iterators are not random access")
++ iterator &operator+=(difference_type j) { std::advance(*this, j); return *this; }
++
++ QT_DEPRECATED_VERSION_X_6_0("Use std::prev or std::advance; QMap iterators are not random access")
++ iterator &operator-=(difference_type j) { std::advance(*this, -j); return *this; }
++
++ QT_DEPRECATED_VERSION_X_6_0("Use std::next; QMap iterators are not random access")
++ //! [qmap-op-step-plus-it]
++ friend iterator operator+(difference_type j, iterator it) { return std::next(it, j); }
++
++ QT_DEPRECATED_VERSION_X_6_0("Use std::prev; QMap iterators are not random access")
++ //! [qmap-op-step-minus-it]
++ friend iterator operator-(difference_type j, iterator it) { return std::prev(it, j); }
++ #endif
++ };
++
++ class const_iterator
++ {
++ friend class QMap<Key, T>;
++ typename Map::const_iterator i;
++ explicit const_iterator(typename Map::const_iterator it) : i(it) {}
++
++ public:
++ using iterator_category = std::bidirectional_iterator_tag;
++ using difference_type = qptrdiff;
++ using value_type = T;
++ using pointer = const T *;
++ using reference = const T &;
++
++ const_iterator() = default;
++ Q_IMPLICIT const_iterator(const iterator &o) : i(o.i) {}
++
++ const Key &key() const { return i->first; }
++ const T &value() const { return i->second; }
++ const T &operator*() const { return i->second; }
++ const T *operator->() const { return &i->second; }
++ friend bool operator==(const const_iterator &lhs, const const_iterator &rhs) { return lhs.i == rhs.i; }
++ friend bool operator!=(const const_iterator &lhs, const const_iterator &rhs) { return lhs.i != rhs.i; }
++
++ const_iterator &operator++()
++ {
++ ++i;
++ return *this;
++ }
++ const_iterator operator++(int)
++ {
++ const_iterator r = *this;
++ ++i;
++ return r;
++ }
++ const_iterator &operator--()
++ {
++ --i;
++ return *this;
++ }
++ const_iterator operator--(int)
++ {
++ const_iterator r = *this;
++ --i;
++ return r;
++ }
++
++ #if QT_DEPRECATED_SINCE(6, 0)
++ QT_DEPRECATED_VERSION_X_6_0("Use std::next; QMap iterators are not random access")
++ //! [qmap-op-it-plus-step-const]
++ friend const_iterator operator+(const_iterator it, difference_type j) { return std::next(it, j); }
++
++ QT_DEPRECATED_VERSION_X_6_0("Use std::prev; QMap iterators are not random access")
++ //! [qmap-op-it-minus-step-const]
++ friend const_iterator operator-(const_iterator it, difference_type j) { return std::prev(it, j); }
++
++ QT_DEPRECATED_VERSION_X_6_0("Use std::next or std::advance; QMap iterators are not random access")
++ const_iterator &operator+=(difference_type j) { std::advance(*this, j); return *this; }
++
++ QT_DEPRECATED_VERSION_X_6_0("Use std::prev or std::advance; QMap iterators are not random access")
++ const_iterator &operator-=(difference_type j) { std::advance(*this, -j); return *this; }
++
++ QT_DEPRECATED_VERSION_X_6_0("Use std::next; QMap iterators are not random access")
++ //! [qmap-op-step-plus-it-const]
++ friend const_iterator operator+(difference_type j, const_iterator it) { return std::next(it, j); }
++
++ QT_DEPRECATED_VERSION_X_6_0("Use std::prev; QMap iterators are not random access")
++ //! [qmap-op-step-minus-it-const]
++ friend const_iterator operator-(difference_type j, const_iterator it) { return std::prev(it, j); }
++ #endif
++ };
++
++ class key_iterator
++ {
++ const_iterator i;
++
++ public:
++ typedef typename const_iterator::iterator_category iterator_category;
++ typedef typename const_iterator::difference_type difference_type;
++ typedef Key value_type;
++ typedef const Key *pointer;
++ typedef const Key &reference;
++
++ key_iterator() = default;
++ explicit key_iterator(const_iterator o) : i(o) { }
++
++ const Key &operator*() const { return i.key(); }
++ const Key *operator->() const { return &i.key(); }
++ bool operator==(key_iterator o) const { return i == o.i; }
++ bool operator!=(key_iterator o) const { return i != o.i; }
++
++ inline key_iterator &operator++() { ++i; return *this; }
++ inline key_iterator operator++(int) { return key_iterator(i++);}
++ inline key_iterator &operator--() { --i; return *this; }
++ inline key_iterator operator--(int) { return key_iterator(i--); }
++ const_iterator base() const { return i; }
++ };
++
++ typedef QKeyValueIterator<const Key&, const T&, const_iterator> const_key_value_iterator;
++ typedef QKeyValueIterator<const Key&, T&, iterator> key_value_iterator;
++
++ // STL style
++ iterator begin() { detach(); return iterator(d->m.begin()); }
++ const_iterator begin() const { if (!d) return const_iterator(); return const_iterator(d->m.cbegin()); }
++ const_iterator constBegin() const { return begin(); }
++ const_iterator cbegin() const { return begin(); }
++ iterator end() { detach(); return iterator(d->m.end()); }
++ const_iterator end() const { if (!d) return const_iterator(); return const_iterator(d->m.end()); }
++ const_iterator constEnd() const { return end(); }
++ const_iterator cend() const { return end(); }
++ key_iterator keyBegin() const { return key_iterator(begin()); }
++ key_iterator keyEnd() const { return key_iterator(end()); }
++ key_value_iterator keyValueBegin() { return key_value_iterator(begin()); }
++ key_value_iterator keyValueEnd() { return key_value_iterator(end()); }
++ const_key_value_iterator keyValueBegin() const { return const_key_value_iterator(begin()); }
++ const_key_value_iterator constKeyValueBegin() const { return const_key_value_iterator(begin()); }
++ const_key_value_iterator keyValueEnd() const { return const_key_value_iterator(end()); }
++ const_key_value_iterator constKeyValueEnd() const { return const_key_value_iterator(end()); }
++ auto asKeyValueRange() & { return QtPrivate::QKeyValueRange<QMap &>(*this); }
++ auto asKeyValueRange() const & { return QtPrivate::QKeyValueRange<const QMap &>(*this); }
++ auto asKeyValueRange() && { return QtPrivate::QKeyValueRange<QMap>(std::move(*this)); }
++ auto asKeyValueRange() const && { return QtPrivate::QKeyValueRange<QMap>(std::move(*this)); }
++
++ iterator erase(const_iterator it)
++ {
++ return erase(it, std::next(it));
++ }
++
++ iterator erase(const_iterator afirst, const_iterator alast)
++ {
++ if (!d)
++ return iterator();
++
++ if (!d.isShared())
++ return iterator(d->m.erase(afirst.i, alast.i));
++
++ auto result = d->erase(afirst.i, alast.i);
++ d.reset(result.data);
++ return iterator(result.it);
++ }
++
++ // more Qt
++ typedef iterator Iterator;
++ typedef const_iterator ConstIterator;
++
++ iterator find(const Key &key)
++ {
++ const auto hold = referenceHoldingDetach();
++ return iterator(d->m.find(key));
++ }
++
++ const_iterator find(const Key &key) const
++ {
++ if (!d)
++ return const_iterator();
++ return const_iterator(d->m.find(key));
++ }
++
++ const_iterator constFind(const Key &key) const
++ {
++ return find(key);
++ }
++
++ iterator lowerBound(const Key &key)
++ {
++ const auto hold = referenceHoldingDetach();
++ return iterator(d->m.lower_bound(key));
++ }
++
++ const_iterator lowerBound(const Key &key) const
++ {
++ if (!d)
++ return const_iterator();
++ return const_iterator(d->m.lower_bound(key));
++ }
++
++ iterator upperBound(const Key &key)
++ {
++ const auto hold = referenceHoldingDetach();
++ return iterator(d->m.upper_bound(key));
++ }
++
++ const_iterator upperBound(const Key &key) const
++ {
++ if (!d)
++ return const_iterator();
++ return const_iterator(d->m.upper_bound(key));
++ }
++
++ iterator insert(const Key &key, const T &value)
++ {
++ const auto hold = referenceHoldingDetachExcept(key);
++ return iterator(d->m.insert_or_assign(key, value).first);
++ }
++
++ iterator insert(const_iterator pos, const Key &key, const T &value)
++ {
++ if (!d) {
++ detach();
++ return iterator(d->m.emplace(key, value).first);
++ } else if (d.isShared()) {
++ auto posDistance = std::distance(d->m.cbegin(), pos.i);
++ const auto hold = referenceHoldingDetachExcept(key);
++ auto dpos = std::next(d->m.cbegin(), posDistance);
++ return iterator(d->m.insert_or_assign(dpos, key, value));
++ }
++ return iterator(d->m.insert_or_assign(pos.i, key, value));
++ }
++
++ void insert(const QMap<Key, T> &map)
++ {
++ if (map.isEmpty())
++ return;
++
++ if (isEmpty()) {
++ *this = map;
++ return;
++ }
++
++ if (d.isShared()) {
++ QtPrivate::QExplicitlySharedDataPointerV2<MapData> newD(new MapData);
++ const auto commit = qScopeGuard([&] { newD.swap(d); });
++ newD->fillWithMergeOf(d->m, map.d->m);
++ return;
++ }
++
++ #ifdef __cpp_lib_node_extract
++ // Since std::map::merge is destructive only use it when not shared
++ auto copy = map.d->m;
++ copy.merge(d->m);
++ d->m = std::move(copy);
++ #else
++ QtPrivate::QExplicitlySharedDataPointerV2<MapData> newD(new MapData);
++ const auto commit = qScopeGuard([&] { newD.swap(d); });
++ newD->fillWithMergeOf(d->m, map.d->m);
++
++ #endif
++ }
++
++ void insert(QMap<Key, T> &&map)
++ {
++ if (map.isEmpty() || map.d.isShared()) {
++ // fall back to a regular copy
++ insert(map);
++ return;
++ }
++
++ // Otherwise insert into map, and do a swap on return
++ const auto commit = qScopeGuard([&] { map.swap(*this); });
++ if (isEmpty())
++ return;
++
++ if (d.isShared()) {
++ map.d->insertMap(d->m);
++ return;
++ }
++
++ #ifdef __cpp_lib_node_extract
++ map.d->m.merge(std::move(d->m));
++ #else
++ // same as above
++ map.d->insertMap(d->m);
++ #endif
++ }
++
++ // STL compatibility
++ [[nodiscard]]
++ inline bool empty() const
++ {
++ return isEmpty();
++ }
++
++ std::pair<iterator, iterator> equal_range(const Key &akey)
++ {
++ const auto hold = referenceHoldingDetach();
++ auto result = d->m.equal_range(akey);
++ return {iterator(result.first), iterator(result.second)};
++ }
++
++ std::pair<const_iterator, const_iterator> equal_range(const Key &akey) const
++ {
++ if (!d)
++ return {};
++ auto result = d->m.equal_range(akey);
++ return {const_iterator(result.first), const_iterator(result.second)};
++ }
++
++ private:
++ #ifdef Q_QDOC
++ friend size_t qHash(const QMap &key, size_t seed = 0);
++ #else
++ # if defined(Q_CC_GHS) || defined (Q_CC_MSVC)
++ // GHS and MSVC tries to intantiate qHash() for the noexcept running into a
++ // non-SFINAE'ed hard error... Create an artificial SFINAE context as a
++ // work-around:
++ template <typename M, std::enable_if_t<std::is_same_v<M, QMap>, bool> = true>
++ friend QtPrivate::QHashMultiReturnType<typename M::key_type, typename M::mapped_type>
++ # else
++ using M = QMap;
++ friend size_t
++ # endif
++ qHash(const M &key, size_t seed = 0)
++ noexcept(QHashPrivate::noexceptPairHash<typename M::key_type, typename M::mapped_type>())
++ {
++ if (!key.d)
++ return seed;
++ // don't use qHashRange to avoid its compile-time overhead:
++ return std::accumulate(key.d->m.begin(), key.d->m.end(), seed,
++ QtPrivate::QHashCombine{seed});
++ }
++ #endif // !Q_QDOC
++ };
++
++ Q_DECLARE_ASSOCIATIVE_ITERATOR(Map)
++ Q_DECLARE_MUTABLE_ASSOCIATIVE_ITERATOR(Map)
++
++ template <typename Key, typename T, typename Predicate>
++ qsizetype erase_if(QMap<Key, T> &map, Predicate pred)
++ {
++ return QtPrivate::associative_erase_if(map, pred);
++ }
++
++
++ //
++ // QMultiMap
++ //
++
++ template <class Key, class T>
++ class QMultiMap
++ {
++ using Map = std::multimap<Key, T>;
++ using MapData = QMapData<Map>;
++ QtPrivate::QExplicitlySharedDataPointerV2<MapData> d;
++
++ // Note: methods that look up a single element by key use lower_bound() rather
++ // than find(). The C++ standard permits std::multimap::find() to return any
++ // element with the matching key; libc++ 22 changed which one it returns.
++ // lower_bound() is where insert() places new elements, so it consistently
++ // identifies the most-recently-inserted one.
++
++ auto findIteratorByKey(const Key &key) const
++ {
++ auto i = d->m.lower_bound(key);
++ const auto &cmp = d->m.key_comp();
++ if (i != d->m.end() && !cmp(key, i->first))
++ return i;
++ return d->m.end();
++ }
++ auto findIteratorByKey(const Key &key)
++ {
++ auto i = d->m.lower_bound(key);
++ const auto &cmp = d->m.key_comp();
++ if (i != d->m.end() && !cmp(key, i->first))
++ return i;
++ return d->m.end();
++ }
++
++ public:
++ using key_type = Key;
++ using mapped_type = T;
++ using difference_type = qptrdiff;
++ using size_type = qsizetype;
++
++ QMultiMap() = default;
++
++ // implicitly generated special member functions are OK!
++
++ QMultiMap(std::initializer_list<std::pair<Key,T>> list)
++ {
++ for (auto &p : list)
++ insert(p.first, p.second);
++ }
++
++ void swap(QMultiMap<Key, T> &other) noexcept
++ {
++ d.swap(other.d);
++ }
++
++ explicit QMultiMap(const QMap<Key, T> &other)
++ : d(other.isEmpty() ? nullptr : new MapData)
++ {
++ if (d) {
++ Q_ASSERT(other.d);
++ d->m.insert(other.d->m.begin(),
++ other.d->m.end());
++ }
++ }
++
++ explicit QMultiMap(QMap<Key, T> &&other)
++ : d(other.isEmpty() ? nullptr : new MapData)
++ {
++ if (d) {
++ Q_ASSERT(other.d);
++ if (other.d.isShared()) {
++ d->m.insert(other.d->m.begin(),
++ other.d->m.end());
++ } else {
++ #ifdef __cpp_lib_node_extract
++ d->m.merge(std::move(other.d->m));
++ #else
++ d->m.insert(std::make_move_iterator(other.d->m.begin()),
++ std::make_move_iterator(other.d->m.end()));
++ #endif
++ }
++ }
++ }
++
++ explicit QMultiMap(const std::multimap<Key, T> &other)
++ : d(other.empty() ? nullptr : new MapData(other))
++ {
++ }
++
++ explicit QMultiMap(std::multimap<Key, T> &&other)
++ : d(other.empty() ? nullptr : new MapData(std::move(other)))
++ {
++ }
++
++ // CHANGE: return type
++ Q_DECL_DEPRECATED_X("Use toStdMultiMap instead")
++ std::multimap<Key, T> toStdMap() const
++ {
++ return toStdMultiMap();
++ }
++
++ std::multimap<Key, T> toStdMultiMap() const &
++ {
++ if (d)
++ return d->m;
++ return {};
++ }
++
++ std::multimap<Key, T> toStdMultiMap() &&
++ {
++ if (d) {
++ if (d.isShared())
++ return d->m;
++ else
++ return std::move(d->m);
++ }
++
++ return {};
++ }
++
++ #ifndef Q_QDOC
++ private:
++ template <typename AKey = Key, typename AT = T,
++ QTypeTraits::compare_eq_result_container<QMultiMap, AKey, AT> = true>
++ friend bool comparesEqual(const QMultiMap &lhs, const QMultiMap &rhs)
++ {
++ if (lhs.d == rhs.d)
++ return true;
++ if (!lhs.d)
++ return rhs == lhs;
++ Q_ASSERT(lhs.d);
++ return rhs.d ? (lhs.d->m == rhs.d->m) : lhs.d->m.empty();
++ }
++ QT_DECLARE_EQUALITY_OPERATORS_HELPER(QMultiMap, QMultiMap, /* non-constexpr */, noexcept(false),
++ template <typename AKey = Key, typename AT = T,
++ QTypeTraits::compare_eq_result_container<QMultiMap, AKey, AT> = true>)
++
++ template <typename AKey = Key, typename AT = T,
++ QtPrivate::if_map_has_relational_operators<QMultiMap, AKey, AT> = true>
++ friend auto compareThreeWay(const QMultiMap &lhs, const QMultiMap &rhs)
++ {
++ return QtOrderingPrivate::lexicographicalCompareThreeWay(lhs.constKeyValueBegin(),
++ lhs.constKeyValueEnd(),
++ rhs.constKeyValueBegin(),
++ rhs.constKeyValueEnd());
++ }
++ QT_DECLARE_ORDERING_HELPER_AUTO(QMultiMap, QMultiMap, /* non-constexpr */, noexcept(false),
++ template <typename AKey = Key, typename AT = T,
++ QtPrivate::if_map_has_relational_operators<QMultiMap, AKey, AT> = true>)
++ public:
++ #else
++ friend bool operator==(const QMultiMap &lhs, const QMultiMap &rhs);
++ friend bool operator!=(const QMultiMap &lhs, const QMultiMap &rhs);
++ friend bool operator<(const QMultiMap &lhs, const QMultiMap &rhs);
++ friend bool operator>(const QMultiMap &lhs, const QMultiMap &rhs);
++ friend bool operator<=(const QMultiMap &lhs, const QMultiMap &rhs);
++ friend bool operator>=(const QMultiMap &lhs, const QMultiMap &rhs);
++ friend auto operator<=>(const QMultiMap &lhs, const QMultiMap &rhs);
++ #endif // Q_QDOC
++
++ size_type size() const { return d ? size_type(d->m.size()) : size_type(0); }
++
++ [[nodiscard]]
++ bool isEmpty() const { return d ? d->m.empty() : true; }
++
++ void detach()
++ {
++ if (d)
++ d.detach();
++ else
++ d.reset(new MapData);
++ }
++
++ // A detach for holding an already shared copy, until calling function
++ // is done using references to keys or values that might reference it.
++ [[nodiscard]] QMultiMap referenceHoldingDetach()
++ {
++ if (!d) {
++ d.reset(new MapData);
++ } else if (d.isShared()) {
++ auto hold = *this;
++ d.detach();
++ return hold;
++ }
++ return {};
++ }
++
++ // Specialized version of referenceHoldingDetach(), which will not copy skipit, if copying
++ [[nodiscard]] QMultiMap referenceHoldingDetachExceptFor(const typename Map::iterator &skipit)
++ {
++ Q_ASSERT(d.isShared());
++ auto hold = *this;
++ QtPrivate::QExplicitlySharedDataPointerV2<MapData> newData(new MapData);
++ newData->copyExceptFor(d->m, skipit);
++ d.swap(newData);
++ return hold;
++ }
++
++ bool isDetached() const noexcept
++ {
++ return d ? !d.isShared() : false; // false makes little sense, but that's shared_null's behavior...
++ }
++
++ bool isSharedWith(const QMultiMap<Key, T> &other) const noexcept
++ {
++ return d == other.d; // also this makes little sense?
++ }
++
++ void clear()
++ {
++ if (!d)
++ return;
++
++ if (!d.isShared())
++ d->m.clear();
++ else
++ d.reset();
++ }
++
++ size_type remove(const Key &key)
++ {
++ if (!d)
++ return 0;
++
++ if (!d.isShared())
++ return size_type(d->m.erase(key));
++
++ MapData *newData = new MapData;
++ size_type result = newData->copyIfNotEquivalentTo(d->m, key).count;
++
++ d.reset(newData);
++
++ return result;
++ }
++
++ size_type remove(const Key &key, const T &value)
++ {
++ if (!d)
++ return 0;
++
++ size_type result = 0;
++ const auto &keyCompare = d->m.key_comp();
++
++ if (d.isShared()) {
++ QtPrivate::QExplicitlySharedDataPointerV2<MapData> newData(new MapData);
++ const auto keep = [&newData](auto it) { newData->m.insert(newData->m.cend(), *it); };
++
++ auto it = d->m.cbegin();
++ const auto end = d->m.cend();
++ for (; it != end && keyCompare(it->first, key); ++it)
++ keep(it);
++ // Keep matching keys if value match, otherwise skip and count
++ for (; it != end && !keyCompare(key, it->first); ++it) {
++ if (!(it->second == value))
++ keep(it);
++ else
++ ++result;
++ }
++ for (; it != end; ++it)
++ keep(it);
++
++ d.swap(newData);
++ return result;
++ }
++
++ // d->m.erase_if(....) would be nice, but that's C++20.
++ // So let's do like find(keyCopy, valueCopy):
++ auto [i, e] = d->m.equal_range(key);
++ if (i == e)
++ return result;
++
++ // value may belong to this map. As such, we need to copy it to ensure
++ // it stays valid throughout the iteration below (which may destroy it)
++ const T valueCopy = value;
++ while (i != e) {
++ if (i->second == valueCopy) {
++ i = d->m.erase(i);
++ ++result;
++ } else {
++ ++i;
++ }
++ }
++
++ return result;
++ }
++
++ template <typename Predicate>
++ size_type removeIf(Predicate pred)
++ {
++ return QtPrivate::associative_erase_if(*this, pred);
++ }
++
++ T take(const Key &key)
++ {
++ if (!d)
++ return T();
++
++ auto i = findIteratorByKey(key);
++ if (d.isShared()) {
++ if (i == d->m.end()) {
++ // NB: take always detaches, even if the key isn't found. This is for historic reasons.
++ detach();
++ return T();
++ }
++ const auto hold = referenceHoldingDetachExceptFor(i);
++ return i->second;
++ }
++
++ if (i == d->m.end())
++ return T();
++
++ #ifdef __cpp_lib_node_extract
++ return std::move(d->m.extract(i).mapped());
++ #else
++ // ### breaks RVO on most compilers (but only on old-fashioned ones, so who cares?)
++ T result(std::move(i->second));
++ d->m.erase(i);
++ return result;
++ #endif
++ }
++
++ bool contains(const Key &key) const
++ {
++ if (!d)
++ return false;
++ auto i = d->m.find(key);
++ return i != d->m.end();
++ }
++
++ bool contains(const Key &key, const T &value) const
++ {
++ return find(key, value) != end();
++ }
++
++ Key key(const T &value, const Key &defaultKey = Key()) const
++ {
++ if (!d)
++ return defaultKey;
++
++ return d->key(value, defaultKey);
++ }
++
++ T value(const Key &key, const T &defaultValue = T()) const
++ {
++ if (!d)
++ return defaultValue;
++ auto i = findIteratorByKey(key);
++ if (i != d->m.cend())
++ return i->second;
++ return defaultValue;
++ }
++
++ QList<Key> keys() const
++ {
++ if (!d)
++ return {};
++ return d->keys();
++ }
++
++ QList<Key> keys(const T &value) const
++ {
++ if (!d)
++ return {};
++ return d->keys(value);
++ }
++
++ QList<Key> uniqueKeys() const
++ {
++ QList<Key> result;
++ if (!d)
++ return result;
++
++ result.reserve(size());
++
++ std::unique_copy(keyBegin(), keyEnd(),
++ std::back_inserter(result));
++
++ result.shrink_to_fit();
++ return result;
++ }
++
++ QList<T> values() const
++ {
++ if (!d)
++ return {};
++ return d->values();
++ }
++
++ QList<T> values(const Key &key) const
++ {
++ QList<T> result;
++ const auto range = equal_range(key);
++ result.reserve(std::distance(range.first, range.second));
++ std::copy(range.first, range.second, std::back_inserter(result));
++ return result;
++ }
++
++ size_type count(const Key &key) const
++ {
++ if (!d)
++ return 0;
++ return d->count(key);
++ }
++
++ size_type count(const Key &key, const T &value) const
++ {
++ if (!d)
++ return 0;
++
++ // TODO: improve; no need of scanning the equal_range twice.
++ auto range = d->m.equal_range(key);
++
++ return size_type(std::count_if(range.first,
++ range.second,
++ MapData::valueIsEqualTo(value)));
++ }
++
++ inline const Key &firstKey() const { Q_ASSERT(!isEmpty()); return constBegin().key(); }
++ inline const Key &lastKey() const { Q_ASSERT(!isEmpty()); return std::next(constEnd(), -1).key(); }
++
++ inline T &first() { Q_ASSERT(!isEmpty()); return *begin(); }
++ inline const T &first() const { Q_ASSERT(!isEmpty()); return *constBegin(); }
++ inline T &last() { Q_ASSERT(!isEmpty()); return *std::next(end(), -1); }
++ inline const T &last() const { Q_ASSERT(!isEmpty()); return *std::next(constEnd(), -1); }
++
++ class const_iterator;
++
++ class iterator
++ {
++ friend class QMultiMap<Key, T>;
++ friend class const_iterator;
++
++ typename Map::iterator i;
++ explicit iterator(typename Map::iterator it) : i(it) {}
++ public:
++ using iterator_category = std::bidirectional_iterator_tag;
++ using difference_type = qptrdiff;
++ using value_type = T;
++ using pointer = T *;
++ using reference = T &;
++
++ iterator() = default;
++
++ const Key &key() const { return i->first; }
++ T &value() const { return i->second; }
++ T &operator*() const { return i->second; }
++ T *operator->() const { return &i->second; }
++ friend bool operator==(const iterator &lhs, const iterator &rhs) { return lhs.i == rhs.i; }
++ friend bool operator!=(const iterator &lhs, const iterator &rhs) { return lhs.i != rhs.i; }
++
++ iterator &operator++()
++ {
++ ++i;
++ return *this;
++ }
++ iterator operator++(int)
++ {
++ iterator r = *this;
++ ++i;
++ return r;
++ }
++ iterator &operator--()
++ {
++ --i;
++ return *this;
++ }
++ iterator operator--(int)
++ {
++ iterator r = *this;
++ --i;
++ return r;
++ }
++
++ #if QT_DEPRECATED_SINCE(6, 0)
++ QT_DEPRECATED_VERSION_X_6_0("Use std::next; QMultiMap iterators are not random access")
++ //! [qmultimap-op-it-plus-step]
++ friend iterator operator+(iterator it, difference_type j) { return std::next(it, j); }
++
++ QT_DEPRECATED_VERSION_X_6_0("Use std::prev; QMultiMap iterators are not random access")
++ //! [qmultimap-op-it-minus-step]
++ friend iterator operator-(iterator it, difference_type j) { return std::prev(it, j); }
++
++ QT_DEPRECATED_VERSION_X_6_0("Use std::next or std::advance; QMultiMap iterators are not random access")
++ iterator &operator+=(difference_type j) { std::advance(*this, j); return *this; }
++
++ QT_DEPRECATED_VERSION_X_6_0("Use std::prev or std::advance; QMultiMap iterators are not random access")
++ iterator &operator-=(difference_type j) { std::advance(*this, -j); return *this; }
++
++ QT_DEPRECATED_VERSION_X_6_0("Use std::next; QMultiMap iterators are not random access")
++ //! [qmultimap-op-step-plus-it]
++ friend iterator operator+(difference_type j, iterator it) { return std::next(it, j); }
++
++ QT_DEPRECATED_VERSION_X_6_0("Use std::prev; QMultiMap iterators are not random access")
++ //! [qmultimap-op-step-minus-it]
++ friend iterator operator-(difference_type j, iterator it) { return std::prev(it, j); }
++ #endif
++ };
++
++ class const_iterator
++ {
++ friend class QMultiMap<Key, T>;
++ typename Map::const_iterator i;
++ explicit const_iterator(typename Map::const_iterator it) : i(it) {}
++
++ public:
++ using iterator_category = std::bidirectional_iterator_tag;
++ using difference_type = qptrdiff;
++ using value_type = T;
++ using pointer = const T *;
++ using reference = const T &;
++
++ const_iterator() = default;
++ Q_IMPLICIT const_iterator(const iterator &o) : i(o.i) {}
++
++ const Key &key() const { return i->first; }
++ const T &value() const { return i->second; }
++ const T &operator*() const { return i->second; }
++ const T *operator->() const { return &i->second; }
++ friend bool operator==(const const_iterator &lhs, const const_iterator &rhs) { return lhs.i == rhs.i; }
++ friend bool operator!=(const const_iterator &lhs, const const_iterator &rhs) { return lhs.i != rhs.i; }
++
++ const_iterator &operator++()
++ {
++ ++i;
++ return *this;
++ }
++ const_iterator operator++(int)
++ {
++ const_iterator r = *this;
++ ++i;
++ return r;
++ }
++ const_iterator &operator--()
++ {
++ --i;
++ return *this;
++ }
++ const_iterator operator--(int)
++ {
++ const_iterator r = *this;
++ --i;
++ return r;
++ }
++
++ #if QT_DEPRECATED_SINCE(6, 0)
++ QT_DEPRECATED_VERSION_X_6_0("Use std::next; QMultiMap iterators are not random access")
++ //! [qmultimap-op-it-plus-step-const]
++ friend const_iterator operator+(const_iterator it, difference_type j) { return std::next(it, j); }
++
++ QT_DEPRECATED_VERSION_X_6_0("Use std::prev; QMultiMap iterators are not random access")
++ //! [qmultimap-op-it-minus-step-const]
++ friend const_iterator operator-(const_iterator it, difference_type j) { return std::prev(it, j); }
++
++ QT_DEPRECATED_VERSION_X_6_0("Use std::next or std::advance; QMultiMap iterators are not random access")
++ const_iterator &operator+=(difference_type j) { std::advance(*this, j); return *this; }
++
++ QT_DEPRECATED_VERSION_X_6_0("Use std::prev or std::advance; QMultiMap iterators are not random access")
++ const_iterator &operator-=(difference_type j) { std::advance(*this, -j); return *this; }
++
++ QT_DEPRECATED_VERSION_X_6_0("Use std::next; QMultiMap iterators are not random access")
++ //! [qmultimap-op-step-plus-it-const]
++ friend const_iterator operator+(difference_type j, const_iterator it) { return std::next(it, j); }
++
++ QT_DEPRECATED_VERSION_X_6_0("Use std::prev; QMultiMap iterators are not random access")
++ //! [qmultimap-op-step-minus-it-const]
++ friend const_iterator operator-(difference_type j, const_iterator it) { return std::prev(it, j); }
++ #endif
++ };
++
++ class key_iterator
++ {
++ const_iterator i;
++
++ public:
++ typedef typename const_iterator::iterator_category iterator_category;
++ typedef typename const_iterator::difference_type difference_type;
++ typedef Key value_type;
++ typedef const Key *pointer;
++ typedef const Key &reference;
++
++ key_iterator() = default;
++ explicit key_iterator(const_iterator o) : i(o) { }
++
++ const Key &operator*() const { return i.key(); }
++ const Key *operator->() const { return &i.key(); }
++ bool operator==(key_iterator o) const { return i == o.i; }
++ bool operator!=(key_iterator o) const { return i != o.i; }
++
++ inline key_iterator &operator++() { ++i; return *this; }
++ inline key_iterator operator++(int) { return key_iterator(i++);}
++ inline key_iterator &operator--() { --i; return *this; }
++ inline key_iterator operator--(int) { return key_iterator(i--); }
++ const_iterator base() const { return i; }
++ };
++
++ typedef QKeyValueIterator<const Key&, const T&, const_iterator> const_key_value_iterator;
++ typedef QKeyValueIterator<const Key&, T&, iterator> key_value_iterator;
++
++ // STL style
++ iterator begin() { detach(); return iterator(d->m.begin()); }
++ const_iterator begin() const { if (!d) return const_iterator(); return const_iterator(d->m.cbegin()); }
++ const_iterator constBegin() const { return begin(); }
++ const_iterator cbegin() const { return begin(); }
++ iterator end() { detach(); return iterator(d->m.end()); }
++ const_iterator end() const { if (!d) return const_iterator(); return const_iterator(d->m.end()); }
++ const_iterator constEnd() const { return end(); }
++ const_iterator cend() const { return end(); }
++ key_iterator keyBegin() const { return key_iterator(begin()); }
++ key_iterator keyEnd() const { return key_iterator(end()); }
++ key_value_iterator keyValueBegin() { return key_value_iterator(begin()); }
++ key_value_iterator keyValueEnd() { return key_value_iterator(end()); }
++ const_key_value_iterator keyValueBegin() const { return const_key_value_iterator(begin()); }
++ const_key_value_iterator constKeyValueBegin() const { return const_key_value_iterator(begin()); }
++ const_key_value_iterator keyValueEnd() const { return const_key_value_iterator(end()); }
++ const_key_value_iterator constKeyValueEnd() const { return const_key_value_iterator(end()); }
++ auto asKeyValueRange() & { return QtPrivate::QKeyValueRange<QMultiMap &>(*this); }
++ auto asKeyValueRange() const & { return QtPrivate::QKeyValueRange<const QMultiMap &>(*this); }
++ auto asKeyValueRange() && { return QtPrivate::QKeyValueRange<QMultiMap>(std::move(*this)); }
++ auto asKeyValueRange() const && { return QtPrivate::QKeyValueRange<QMultiMap>(std::move(*this)); }
++
++ iterator erase(const_iterator it)
++ {
++ return erase(it, std::next(it));
++ }
++
++ iterator erase(const_iterator afirst, const_iterator alast)
++ {
++ if (!d)
++ return iterator();
++
++ if (!d.isShared())
++ return iterator(d->m.erase(afirst.i, alast.i));
++
++ auto result = d->erase(afirst.i, alast.i);
++ d.reset(result.data);
++ return iterator(result.it);
++ }
++
++ // more Qt
++ typedef iterator Iterator;
++ typedef const_iterator ConstIterator;
++
++ size_type count() const
++ {
++ return size();
++ }
++
++ iterator find(const Key &key)
++ {
++ const auto hold = referenceHoldingDetach();
++ return iterator(findIteratorByKey(key));
++ }
++
++ const_iterator find(const Key &key) const
++ {
++ if (!d)
++ return const_iterator();
++ return const_iterator(findIteratorByKey(key));
++ }
++
++ const_iterator constFind(const Key &key) const
++ {
++ return find(key);
++ }
++
++ iterator find(const Key &key, const T &value)
++ {
++ const auto hold = referenceHoldingDetach();
++
++ auto range = d->m.equal_range(key);
++ auto i = std::find_if(range.first, range.second,
++ MapData::valueIsEqualTo(value));
++
++ if (i != range.second)
++ return iterator(i);
++ return iterator(d->m.end());
++ }
++
++ const_iterator find(const Key &key, const T &value) const
++ {
++ if (!d)
++ return const_iterator();
++
++ auto range = d->m.equal_range(key);
++ auto i = std::find_if(range.first, range.second,
++ MapData::valueIsEqualTo(value));
++
++ if (i != range.second)
++ return const_iterator(i);
++ return const_iterator(d->m.end());
++ }
++
++ const_iterator constFind(const Key &key, const T &value) const
++ {
++ return find(key, value);
++ }
++
++ iterator lowerBound(const Key &key)
++ {
++ const auto hold = referenceHoldingDetach();
++ return iterator(d->m.lower_bound(key));
++ }
++
++ const_iterator lowerBound(const Key &key) const
++ {
++ if (!d)
++ return const_iterator();
++ return const_iterator(d->m.lower_bound(key));
++ }
++
++ iterator upperBound(const Key &key)
++ {
++ const auto hold = referenceHoldingDetach();
++ return iterator(d->m.upper_bound(key));
++ }
++
++ const_iterator upperBound(const Key &key) const
++ {
++ if (!d)
++ return const_iterator();
++ return const_iterator(d->m.upper_bound(key));
++ }
++
++ iterator insert(const Key &key, const T &value)
++ {
++ const auto hold = referenceHoldingDetach();
++ // note that std::multimap inserts at the end of an equal_range for a key,
++ // QMultiMap at the beginning.
++ auto i = d->m.lower_bound(key);
++ return iterator(d->m.insert(i, {key, value}));
++ }
++
++ iterator insert(const_iterator pos, const Key &key, const T &value)
++ {
++ if (!d) {
++ d.reset(new MapData);
++ return iterator(d->m.insert({ key, value }));
++ } else if (d.isShared()) {
++ auto posDistance = std::distance(d->m.cbegin(), pos.i);
++ auto hold = referenceHoldingDetach();
++ auto dpos = std::next(d->m.cbegin(), posDistance);
++ return iterator(d->m.insert(dpos, {key, value}));
++ }
++
++ return iterator(d->m.insert(pos.i, {key, value}));
++ }
++
++ #if QT_DEPRECATED_SINCE(6, 0)
++ QT_DEPRECATED_VERSION_X_6_0("Use insert() instead")
++ iterator insertMulti(const Key &key, const T &value)
++ {
++ return insert(key, value);
++ }
++ QT_DEPRECATED_VERSION_X_6_0("Use insert() instead")
++ iterator insertMulti(const_iterator pos, const Key &key, const T &value)
++ {
++ return insert(pos, key, value);
++ }
++
++ QT_DEPRECATED_VERSION_X_6_0("Use unite() instead")
++ void insert(const QMultiMap<Key, T> &map)
++ {
++ unite(map);
++ }
++
++ QT_DEPRECATED_VERSION_X_6_0("Use unite() instead")
++ void insert(QMultiMap<Key, T> &&map)
++ {
++ unite(std::move(map));
++ }
++ #endif
++
++ iterator replace(const Key &key, const T &value)
++ {
++ if (!d) {
++ d.reset(new MapData);
++ return iterator(d->m.insert({ key, value }));
++ }
++ auto i = findIteratorByKey(key);
++ if (d.isShared()) {
++ const auto hold = referenceHoldingDetachExceptFor(i);
++ return iterator(d->m.insert({ key, value }));
++ }
++
++ // Similarly, improve here (e.g. lower_bound and hinted insert);
++ // there's no insert_or_assign on multimaps
++ if (i != d->m.end())
++ i->second = value;
++ else
++ i = d->m.insert({key, value});
++
++ return iterator(i);
++ }
++
++ // STL compatibility
++ [[nodiscard]]
++ inline bool empty() const { return isEmpty(); }
++
++ std::pair<iterator, iterator> equal_range(const Key &akey)
++ {
++ const auto hold = referenceHoldingDetach();
++ auto result = d->m.equal_range(akey);
++ return {iterator(result.first), iterator(result.second)};
++ }
++
++ std::pair<const_iterator, const_iterator> equal_range(const Key &akey) const
++ {
++ if (!d)
++ return {};
++ auto result = d->m.equal_range(akey);
++ return {const_iterator(result.first), const_iterator(result.second)};
++ }
++
++ QMultiMap &unite(const QMultiMap &other)
++ {
++ if (other.isEmpty())
++ return *this;
++
++ detach();
++
++ auto copy = other.d->m;
++ #ifdef __cpp_lib_node_extract
++ copy.merge(std::move(d->m));
++ #else
++ copy.insert(std::make_move_iterator(d->m.begin()),
++ std::make_move_iterator(d->m.end()));
++ #endif
++ d->m = std::move(copy);
++ return *this;
++ }
++
++ QMultiMap &unite(QMultiMap<Key, T> &&other)
++ {
++ if (!other.d || other.d->m.empty())
++ return *this;
++
++ if (other.d.isShared()) {
++ // fall back to a regular copy
++ unite(other);
++ return *this;
++ }
++
++ detach();
++
++ #ifdef __cpp_lib_node_extract
++ other.d->m.merge(std::move(d->m));
++ #else
++ other.d->m.insert(std::make_move_iterator(d->m.begin()),
++ std::make_move_iterator(d->m.end()));
++ #endif
++ *this = std::move(other);
++ return *this;
++ }
++ };
++
++ Q_DECLARE_ASSOCIATIVE_ITERATOR(MultiMap)
++ Q_DECLARE_MUTABLE_ASSOCIATIVE_ITERATOR(MultiMap)
++
++ template <typename Key, typename T>
++ QMultiMap<Key, T> operator+(const QMultiMap<Key, T> &lhs, const QMultiMap<Key, T> &rhs)
++ {
++ auto result = lhs;
++ result += rhs;
++ return result;
++ }
++
++ template <typename Key, typename T>
++ QMultiMap<Key, T> operator+=(QMultiMap<Key, T> &lhs, const QMultiMap<Key, T> &rhs)
++ {
++ return lhs.unite(rhs);
++ }
++
++ template <typename Key, typename T, typename Predicate>
++ qsizetype erase_if(QMultiMap<Key, T> &map, Predicate pred)
++ {
++ return QtPrivate::associative_erase_if(map, pred);
++ }
++
++ QT_END_NAMESPACE
++
++ #endif // QMAP_H