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:
parent
8e3f04f54c
commit
f81fd7bd9f
@ -2377,7 +2377,7 @@ size_t qHash(long double key, size_t seed) noexcept
|
|||||||
// [use result.iterator...]
|
// [use result.iterator...]
|
||||||
\endcode
|
\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
|
that prevented the insertion, and a boolean, \l{QHash::TryEmplaceResult::}{inserted}, denoting
|
||||||
whether the insertion took place.
|
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.
|
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.
|
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)
|
/*! \fn template <class Key, class T> void QHash<Key, T>::insert(const QHash &other)
|
||||||
|
@ -1456,6 +1456,48 @@ private:
|
|||||||
}
|
}
|
||||||
return {iterator(bucket.toIterator(d)), shouldInsert};
|
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:
|
public:
|
||||||
|
|
||||||
float load_factor() const noexcept { return d ? d->loadFactor() : 0; }
|
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);
|
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);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -498,6 +498,14 @@ private Q_SLOTS:
|
|||||||
{ comparisonTest_impl<QVarLengthArray<const int *>, Qt::strong_ordering>(); }
|
{ comparisonTest_impl<QVarLengthArray<const int *>, Qt::strong_ordering>(); }
|
||||||
void comparisonTest_QVarLengthArray_LessOnly()
|
void comparisonTest_QVarLengthArray_LessOnly()
|
||||||
{ comparisonTest_impl<QVarLengthArray<LessOnly>, Qt::weak_ordering>(); }
|
{ 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()
|
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)
|
QTEST_APPLESS_MAIN(tst_ContainerApiSymmetry)
|
||||||
#include "tst_containerapisymmetry.moc"
|
#include "tst_containerapisymmetry.moc"
|
||||||
|
@ -84,6 +84,8 @@ private slots:
|
|||||||
void emplace();
|
void emplace();
|
||||||
void tryEmplace();
|
void tryEmplace();
|
||||||
|
|
||||||
|
void insertOrAssign();
|
||||||
|
|
||||||
void badHashFunction();
|
void badHashFunction();
|
||||||
void hashOfHash();
|
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 {
|
struct BadKey {
|
||||||
int k;
|
int k;
|
||||||
BadKey(int i) : k(i) {}
|
BadKey(int i) : k(i) {}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user