diff --git a/src/corelib/tools/qhash.cpp b/src/corelib/tools/qhash.cpp index 79520305836..e2dc4486199 100644 --- a/src/corelib/tools/qhash.cpp +++ b/src/corelib/tools/qhash.cpp @@ -2377,7 +2377,7 @@ size_t qHash(long double key, size_t seed) noexcept // [use result.iterator...] \endcode - \sa emplace(), tryInsert() + \sa emplace(), tryInsert(), insertOrAssign() */ /*! @@ -2393,7 +2393,7 @@ size_t qHash(long double key, size_t seed) noexcept that prevented the insertion, and a boolean, \l{QHash::TryEmplaceResult::}{inserted}, denoting whether the insertion took place. - \sa insert(), tryEmplace() + \sa insert(), tryEmplace(), insertOrAssign() */ /*! @@ -2412,7 +2412,7 @@ size_t qHash(long double key, size_t seed) noexcept These functions are provided for compatibility with the standard library. - \sa emplace(), tryEmplace(), tryInsert() + \sa emplace(), tryEmplace(), tryInsert(), insertOrAssign() */ /*! @@ -2430,7 +2430,62 @@ size_t qHash(long double key, size_t seed) noexcept These functions are provided for compatibility with the standard library. - \sa emplace(), tryEmplace(), tryInsert() + \sa emplace(), tryEmplace(), tryInsert(), insertOrAssign() +*/ + +/*! + \fn template template QHash::TryEmplaceResult QHash::insertOrAssign(const Key &key, Value &&value) + \fn template template QHash::TryEmplaceResult QHash::insertOrAssign(Key &&key, Value &&value) + \fn template template = true, if_key_constructible_from = true> QHash::TryEmplaceResult QHash::insertOrAssign(K &&key, Value &&value) + \since 6.9 + + Attempts to insert an item with the \a key and \a value. + If an item with \a key already exists its value is overwritten with \a value. + + Returns an instance of \l{TryEmplaceResult}, a structure that holds an + \l{QHash::TryEmplaceResult::}{iterator} to the item, and a boolean, + \l{QHash::TryEmplaceResult::}{inserted}, denoting whether the item was newly created (\c{true}) + or if it previously existed (\c{false}). + + \sa insert(), tryEmplace(), tryInsert() +*/ + +/*! + \fn template template std::pair::key_value_iterator, bool> QHash::insert_or_assign(const Key &key, Value &&value) + \fn template template std::pair::key_value_iterator, bool> QHash::insert_or_assign(Key &&key, Value &&value) + \fn template template = true, if_key_constructible_from = true> std::pair::key_value_iterator, bool> QHash::insert_or_assign(K &&key, Value &&value) + \since 6.9 + + Attempts to insert an item with the \a key and \a value. + If an item with \a key already exists its value is overwritten with \a value. + + Returns a pair consisting of an iterator pointing to the item, and a + boolean, denoting whether the item was newly created (\c{true}) or if it + previously existed (\c{false}). + + These functions are provided for compatibility with the standard library. + + \sa insert(), tryEmplace(), tryInsert(), insertOrAssign() +*/ + +/*! + \fn template template std::pair::key_value_iterator, bool> QHash::insert_or_assign(const_iterator hint, const Key &key, Value &&value) + \fn template template std::pair::key_value_iterator, bool> QHash::insert_or_assign(const_iterator hint, Key &&key, Value &&value) + \fn template template = true, if_key_constructible_from = true> std::pair::key_value_iterator, bool> QHash::insert_or_assign(const_iterator hint, K &&key, Value &&value) + \since 6.9 + + Attempts to insert an item with the \a key and \a value. + If an item with \a key already exists its value is overwritten with \a value. + + Returns a pair consisting of an iterator pointing to the item, and a + boolean, denoting whether the item was newly created (\c{true}) or if it + previously existed (\c{false}). + + \a hint is ignored. + + These functions are provided for compatibility with the standard library. + + \sa insert(), tryEmplace(), insertOrAssign() */ /*! \fn template void QHash::insert(const QHash &other) diff --git a/src/corelib/tools/qhash.h b/src/corelib/tools/qhash.h index e544b5f870a..c77d2d76589 100644 --- a/src/corelib/tools/qhash.h +++ b/src/corelib/tools/qhash.h @@ -1456,6 +1456,48 @@ private: } return {iterator(bucket.toIterator(d)), shouldInsert}; } +public: + template + TryEmplaceResult insertOrAssign(const Key &key, Value &&value) + { + return insertOrAssign_impl(key, std::forward(value)); + } + template + TryEmplaceResult insertOrAssign(Key &&key, Value &&value) + { + return insertOrAssign_impl(std::move(key), std::forward(value)); + } + template + std::pair insert_or_assign(const Key &key, Value &&value) + { + return insertOrAssign_impl(key, std::forward(value)); + } + template + std::pair insert_or_assign(Key &&key, Value &&value) + { + return insertOrAssign_impl(std::move(key), std::forward(value)); + } + template + key_value_iterator insert_or_assign(const_iterator /*hint*/, const Key &key, Value &&value) + { + return key_value_iterator(insertOrAssign_impl(key, std::forward(value)).iterator); + } + template + key_value_iterator insert_or_assign(const_iterator /*hint*/, Key &&key, Value &&value) + { + return key_value_iterator(insertOrAssign_impl(std::move(key), std::forward(value)).iterator); + } + +private: + template + TryEmplaceResult insertOrAssign_impl(K &&key, Value &&value) + { + auto r = tryEmplace(std::forward(key), std::forward(value)); + if (!r.inserted) + *r.iterator = std::forward(value); // `value` is untouched if we get here + return r; + } + public: float load_factor() const noexcept { return d ? d->loadFactor() : 0; } @@ -1578,6 +1620,21 @@ public: { return key_value_iterator(tryEmplace_impl(std::forward(key), std::forward(args)...).iterator); } + template = true, if_key_constructible_from = true> + TryEmplaceResult insertOrAssign(K &&key, Value &&value) + { + return insertOrAssign_impl(std::forward(key), std::forward(value)); + } + template = true, if_key_constructible_from = true> + std::pair insert_or_assign(K &&key, Value &&value) + { + return insertOrAssign_impl(std::forward(key), std::forward(value)); + } + template = true, if_key_constructible_from = true> + key_value_iterator insert_or_assign(const_iterator /*hint*/, K &&key, Value &&value) + { + return key_value_iterator(insertOrAssign_impl(std::forward(key), std::forward(value)).iterator); + } }; diff --git a/tests/auto/corelib/tools/containerapisymmetry/tst_containerapisymmetry.cpp b/tests/auto/corelib/tools/containerapisymmetry/tst_containerapisymmetry.cpp index 86d9904d91b..ecfcdea4cb7 100644 --- a/tests/auto/corelib/tools/containerapisymmetry/tst_containerapisymmetry.cpp +++ b/tests/auto/corelib/tools/containerapisymmetry/tst_containerapisymmetry.cpp @@ -498,6 +498,14 @@ private Q_SLOTS: { comparisonTest_impl, Qt::strong_ordering>(); } void comparisonTest_QVarLengthArray_LessOnly() { comparisonTest_impl, Qt::weak_ordering>(); } + +private: + template + void insert_or_assign_impl() const; + +private Q_SLOTS: + void insert_or_assign_QHash() { insert_or_assign_impl>(); } + void insert_or_assign_unordered_map() { insert_or_assign_impl>(); } }; void tst_ContainerApiSymmetry::init() @@ -1517,5 +1525,32 @@ void tst_ContainerApiSymmetry::comparisonTest_impl() } } +template +void tst_ContainerApiSymmetry::insert_or_assign_impl() const +{ + using K = typename Container::key_type; + using V = typename Container::mapped_type; + Container c; + auto p = c.insert_or_assign(K(), V()); + QVERIFY(p.second); + QCOMPARE(p.first->first, K()); + QCOMPARE(p.first->second, V()); + + auto it = c.insert_or_assign(c.begin(), K(), V() + 1); + QCOMPARE(it->first, K()); + QCOMPARE(it->second, V() + 1); + + K k{}; + V v{}; + p = c.insert_or_assign(k, v); + QVERIFY(!p.second); + QCOMPARE(p.first->first, K()); + QCOMPARE(p.first->second, V()); + + it = c.insert_or_assign(c.begin(), k, v + 2); + QCOMPARE(it->first, K()); + QCOMPARE(it->second, V() + 2); +} + QTEST_APPLESS_MAIN(tst_ContainerApiSymmetry) #include "tst_containerapisymmetry.moc" diff --git a/tests/auto/corelib/tools/qhash/tst_qhash.cpp b/tests/auto/corelib/tools/qhash/tst_qhash.cpp index e8bcdb275fe..3aa0815464a 100644 --- a/tests/auto/corelib/tools/qhash/tst_qhash.cpp +++ b/tests/auto/corelib/tools/qhash/tst_qhash.cpp @@ -84,6 +84,8 @@ private slots: void emplace(); void tryEmplace(); + void insertOrAssign(); + void badHashFunction(); void hashOfHash(); @@ -2973,6 +2975,93 @@ void tst_QHash::tryEmplace() } } +void tst_QHash::insertOrAssign() +{ + { + QHash hash; + QHash::TryEmplaceResult r = hash.insertOrAssign(0, 0); + QCOMPARE_NE(r.iterator, hash.end()); + QCOMPARE(hash.size(), 1); + QCOMPARE(hash[0], 0); + QVERIFY(r.inserted); + QCOMPARE(*r.iterator, 0); + + int cref = 0; + r = hash.insertOrAssign(cref, 1); + QCOMPARE_NE(r.iterator, hash.end()); + QCOMPARE(hash.size(), 1); + QCOMPARE(hash[0], 1); + QVERIFY(!r.inserted); + QCOMPARE(*r.iterator, 1); + } + // insert_or_assign (stdlib compat) + { + QHash hash; + QHash::TryEmplaceResult r = hash.insert_or_assign(0, 0); + QCOMPARE_NE(r.iterator, hash.end()); + QCOMPARE(hash.size(), 1); + QCOMPARE(hash[0], 0); + QVERIFY(r.inserted); + QCOMPARE(*r.iterator, 0); + + int cref = 0; + r = hash.insert_or_assign(cref, 1); + QCOMPARE_NE(r.iterator, hash.end()); + QCOMPARE(hash.size(), 1); + QCOMPARE(hash[0], 1); + QVERIFY(!r.inserted); + QCOMPARE(*r.iterator, 1); + + auto [it, inserted] = hash.insert_or_assign(1, 1); + QCOMPARE_NE(it, hash.keyValueEnd()); + QCOMPARE(hash.size(), 2); + QCOMPARE(hash[1], 1); + QVERIFY(inserted); + QCOMPARE(it->second, 1); + + cref = 1; + std::tie(it, inserted) = hash.insert_or_assign(cref, -1); + QCOMPARE_NE(it, hash.keyValueEnd()); + QCOMPARE(hash.size(), 2); + QCOMPARE(hash[1], -1); + QVERIFY(!inserted); + QCOMPARE(it->second, -1); + + // And with iterator hint: + it = hash.insert_or_assign(hash.end(), 0, -1); + QCOMPARE_NE(it, hash.keyValueEnd()); + QCOMPARE(hash.size(), 2); + QCOMPARE(hash[1], -1); + QVERIFY(!inserted); + QCOMPARE(it->second, -1); + + it = hash.insert_or_assign(hash.end(), cref, -2); + QCOMPARE_NE(it, hash.keyValueEnd()); + QCOMPARE(hash.size(), 2); + QCOMPARE(hash[1], -2); + QVERIFY(!inserted); + QCOMPARE(it->second, -2); + } + { + // Heterogenous key + QHash hash; + auto r = hash.insertOrAssign(HeterogeneousHashingType{u"Hello World"_s}, 242); + QCOMPARE_NE(r.iterator, hash.end()); + QVERIFY(r.inserted); + QCOMPARE(r.iterator.value(), 242); + QCOMPARE(hash.size(), 1); + QCOMPARE(r.iterator.key(), u"Hello World"_s); + + HeterogeneousHashingType ctor{u"Uniquely"_s}; + auto [it, inserted] = hash.insert_or_assign(ctor, 1); + QCOMPARE_NE(it, hash.keyValueEnd()); + QVERIFY(inserted); + QCOMPARE(it->second, 1); + QCOMPARE(hash.size(), 2); + QCOMPARE(it->first, u"Uniquely"_s); + } +} + struct BadKey { int k; BadKey(int i) : k(i) {}