From f044664c685eb81205c2ebdd78e11367d2d44006 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Fri, 7 Jan 2022 09:44:56 +0100 Subject: [PATCH] QFlatMap: add try_emplace (w/o hint) QFlatMap, like its public brethren, features the broken Qt-style insert() behavior (what the STL calls insert_or_assign()), which makes its insert() unusable for actual STL-style insert() work, with no replacement except the size-check-and-index-operator trick: const auto oldSize = c.size(); auto &e = c[key]; if (c.size() != oldSize) { // inserted } Even though QFlatMap::insert() appears to return the correct info, it's useless, because the old value has been assigned over by the time insert() returns. Pick-to: 6.3 6.2 Change-Id: If4173c42523a128dfd22ab496dde0089ba73f41c Reviewed-by: Qt CI Bot Reviewed-by: Joerg Bornemann --- src/corelib/tools/qflatmap_p.h | 24 ++++++ .../corelib/tools/qflatmap/tst_qflatmap.cpp | 83 +++++++++++++++++++ 2 files changed, 107 insertions(+) diff --git a/src/corelib/tools/qflatmap_p.h b/src/corelib/tools/qflatmap_p.h index 621eb7adba..d7f8ff48cb 100644 --- a/src/corelib/tools/qflatmap_p.h +++ b/src/corelib/tools/qflatmap_p.h @@ -726,6 +726,30 @@ public: } } + template + std::pair try_emplace(const Key &key, Args&&...args) + { + auto it = lower_bound(key); + if (it == end() || key_compare::operator()(key, it.key())) { + c.values.emplace(toValuesIterator(it), std::forward(args)...); + return { fromKeysIterator(c.keys.insert(toKeysIterator(it), key)), true }; + } else { + return {it, false}; + } + } + + template + std::pair try_emplace(Key &&key, Args&&...args) + { + auto it = lower_bound(key); + if (it == end() || key_compare::operator()(key, it.key())) { + c.values.emplace(toValuesIterator(it), std::forward(args)...); + return { fromKeysIterator(c.keys.insert(toKeysIterator(it), std::move(key))), true }; + } else { + return {it, false}; + } + } + template = nullptr> void insert(InputIt first, InputIt last) { diff --git a/tests/auto/corelib/tools/qflatmap/tst_qflatmap.cpp b/tests/auto/corelib/tools/qflatmap/tst_qflatmap.cpp index f8f48d7bfb..af616f0a4c 100644 --- a/tests/auto/corelib/tools/qflatmap/tst_qflatmap.cpp +++ b/tests/auto/corelib/tools/qflatmap/tst_qflatmap.cpp @@ -26,6 +26,8 @@ ** ****************************************************************************/ +#define QT_USE_QSTRINGBUILDER + #include #include @@ -51,6 +53,7 @@ private slots: void iterators(); void statefulComparator(); void transparency(); + void try_emplace(); void viewIterators(); void varLengthArray(); }; @@ -425,6 +428,86 @@ void tst_QFlatMap::transparency() QCOMPARE(m.lower_bound(sv3).value(), "dree"); } +void tst_QFlatMap::try_emplace() +{ + using Map = QFlatMap; + + const QByteArray foo = QByteArrayLiteral("foo"); + const qsizetype qqq_1 = 3; + const char qqq_2 = 'q'; + const QByteArray qqq = QByteArray(qqq_1, qqq_2); + + auto sb = [] (const auto &str) { return str % ""; }; + auto rvalue = [](const auto &x) { return x; }; +#define lvalue(x) x +#define CHECKS() \ + do { \ + QVERIFY(!m.try_emplace(rvalue(foo), lvalue(foo)).second); \ + QCOMPARE(m.value(foo), qqq); \ + QVERIFY(!m.try_emplace(lvalue(foo), lvalue(foo)).second); \ + QCOMPARE(m.value(foo), qqq); \ + QVERIFY(!m.try_emplace(lvalue(foo), sb(foo)).second); \ + QCOMPARE(m.value(foo), qqq); \ + QVERIFY(!m.try_emplace(rvalue(foo), sb(foo)).second); \ + QCOMPARE(m.value(foo), qqq); \ + } while (0) \ + /* end */ + + { + Map m; + QVERIFY(m.try_emplace(lvalue(foo), lvalue(qqq)).second); + CHECKS(); + } + + { + Map m; + QVERIFY(m.try_emplace(lvalue(foo), rvalue(qqq)).second); + CHECKS(); + } + + { + Map m; + QVERIFY(m.try_emplace(lvalue(foo), qqq_1, qqq_2).second); + QCOMPARE(m.value(foo), qqq); + CHECKS(); + } + + { + Map m; + QVERIFY(m.try_emplace(lvalue(foo), sb(qqq)).second); + QCOMPARE(m.value(foo), qqq); + CHECKS(); + } + + { + Map m; + QVERIFY(m.try_emplace(rvalue(foo), lvalue(qqq)).second); + CHECKS(); + } + + { + Map m; + QVERIFY(m.try_emplace(rvalue(foo), rvalue(qqq)).second); + CHECKS(); + } + + { + Map m; + QVERIFY(m.try_emplace(rvalue(foo), qqq_1, qqq_2).second); + QCOMPARE(m.value(foo), qqq); + CHECKS(); + } + + { + Map m; + QVERIFY(m.try_emplace(rvalue(foo), sb(qqq)).second); + QCOMPARE(m.value(foo), qqq); + CHECKS(); + } +#undef CHECKS +#undef lvalue +} + void tst_QFlatMap::viewIterators() { using Map = QFlatMap;