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...]
|
||||
\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)
|
||||
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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) {}
|
||||
|
Loading…
x
Reference in New Issue
Block a user