QHash: add insertOrAssign / insert_or_assign

A version of insert that returns whether or not the operation lead to a
new entry being added or if a previously existing entry was simply
updated.

insert_or_assign variants (including iterator hint versions) are added
for compatibility with the standard library.

[ChangeLog][QtCore][QHash] Added insertOrAssign.

[ChangeLog][QtCore][QHash] Added insert_or_assign for compatibility with
the standard library.

Change-Id: Ife013669e09419e79d4a9f3bbb7152be68223394
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Mårten Nordheim 2024-12-02 11:31:33 +01:00
parent 8e3f04f54c
commit f81fd7bd9f
4 changed files with 240 additions and 4 deletions

View File

@ -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 <class Key, class T> template <typename Value> QHash<Key, T>::TryEmplaceResult QHash<Key, T>::insertOrAssign(const Key &key, Value &&value)
\fn template <class Key, class T> template <typename Value> QHash<Key, T>::TryEmplaceResult QHash<Key, T>::insertOrAssign(Key &&key, Value &&value)
\fn template <class Key, class T> template <typename K, typename Value, if_heterogeneously_searchable<K> = true, if_key_constructible_from<K> = true> QHash<Key, T>::TryEmplaceResult QHash<Key, T>::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 <class Key, class T> template <typename Value> std::pair<QHash<Key, T>::key_value_iterator, bool> QHash<Key, T>::insert_or_assign(const Key &key, Value &&value)
\fn template <class Key, class T> template <typename Value> std::pair<QHash<Key, T>::key_value_iterator, bool> QHash<Key, T>::insert_or_assign(Key &&key, Value &&value)
\fn template <class Key, class T> template <typename K, typename Value, if_heterogeneously_searchable<K> = true, if_key_constructible_from<K> = true> std::pair<QHash<Key, T>::key_value_iterator, bool> QHash<Key, T>::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 <class Key, class T> template <typename Value> std::pair<QHash<Key, T>::key_value_iterator, bool> QHash<Key, T>::insert_or_assign(const_iterator hint, const Key &key, Value &&value)
\fn template <class Key, class T> template <typename Value> std::pair<QHash<Key, T>::key_value_iterator, bool> QHash<Key, T>::insert_or_assign(const_iterator hint, Key &&key, Value &&value)
\fn template <class Key, class T> template <typename K, typename Value, if_heterogeneously_searchable<K> = true, if_key_constructible_from<K> = true> std::pair<QHash<Key, T>::key_value_iterator, bool> QHash<Key, T>::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 <class Key, class T> void QHash<Key, T>::insert(const QHash &other)

View File

@ -1456,6 +1456,48 @@ private:
}
return {iterator(bucket.toIterator(d)), shouldInsert};
}
public:
template <typename Value>
TryEmplaceResult insertOrAssign(const Key &key, Value &&value)
{
return insertOrAssign_impl(key, std::forward<Value>(value));
}
template <typename Value>
TryEmplaceResult insertOrAssign(Key &&key, Value &&value)
{
return insertOrAssign_impl(std::move(key), std::forward<Value>(value));
}
template <typename Value>
std::pair<key_value_iterator, bool> insert_or_assign(const Key &key, Value &&value)
{
return insertOrAssign_impl(key, std::forward<Value>(value));
}
template <typename Value>
std::pair<key_value_iterator, bool> insert_or_assign(Key &&key, Value &&value)
{
return insertOrAssign_impl(std::move(key), std::forward<Value>(value));
}
template <typename Value>
key_value_iterator insert_or_assign(const_iterator /*hint*/, const Key &key, Value &&value)
{
return key_value_iterator(insertOrAssign_impl(key, std::forward<Value>(value)).iterator);
}
template <typename Value>
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>(value)).iterator);
}
private:
template <typename K, typename Value>
TryEmplaceResult insertOrAssign_impl(K &&key, Value &&value)
{
auto r = tryEmplace(std::forward<K>(key), std::forward<Value>(value));
if (!r.inserted)
*r.iterator = std::forward<Value>(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<K>(key), std::forward<Args>(args)...).iterator);
}
template <typename K, typename Value, if_heterogeneously_searchable<K> = true, if_key_constructible_from<K> = true>
TryEmplaceResult insertOrAssign(K &&key, Value &&value)
{
return insertOrAssign_impl(std::forward<K>(key), std::forward<Value>(value));
}
template <typename K, typename Value, if_heterogeneously_searchable<K> = true, if_key_constructible_from<K> = true>
std::pair<key_value_iterator, bool> insert_or_assign(K &&key, Value &&value)
{
return insertOrAssign_impl(std::forward<K>(key), std::forward<Value>(value));
}
template <typename K, typename Value, if_heterogeneously_searchable<K> = true, if_key_constructible_from<K> = true>
key_value_iterator insert_or_assign(const_iterator /*hint*/, K &&key, Value &&value)
{
return key_value_iterator(insertOrAssign_impl(std::forward<K>(key), std::forward<Value>(value)).iterator);
}
};

View File

@ -498,6 +498,14 @@ private Q_SLOTS:
{ comparisonTest_impl<QVarLengthArray<const int *>, Qt::strong_ordering>(); }
void comparisonTest_QVarLengthArray_LessOnly()
{ comparisonTest_impl<QVarLengthArray<LessOnly>, Qt::weak_ordering>(); }
private:
template <typename Container>
void insert_or_assign_impl() const;
private Q_SLOTS:
void insert_or_assign_QHash() { insert_or_assign_impl<QHash<int, int>>(); }
void insert_or_assign_unordered_map() { insert_or_assign_impl<std::unordered_map<int, int>>(); }
};
void tst_ContainerApiSymmetry::init()
@ -1517,5 +1525,32 @@ void tst_ContainerApiSymmetry::comparisonTest_impl()
}
}
template <typename Container>
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"

View File

@ -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<int, int> hash;
QHash<int, int>::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<int, int> hash;
QHash<int, int>::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<QString, int> 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) {}