From 5d7be66fd742e694576ce722aa6e0766c23942f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A5rten=20Nordheim?= Date: Wed, 20 May 2020 13:59:48 +0200 Subject: [PATCH] QList: Add a append(QList &&) overload We already had append(const QList &) and now there's an overload taking an rvalue reference. Change-Id: Id2fbc6c57badebebeee7b80d15bb333270fa4e19 Reviewed-by: Thiago Macieira --- src/corelib/tools/qlist.h | 33 ++++ src/corelib/tools/qlist.qdoc | 31 +++ tests/auto/corelib/tools/qlist/tst_qlist.cpp | 195 +++++++++++++++++++ 3 files changed, 259 insertions(+) diff --git a/src/corelib/tools/qlist.h b/src/corelib/tools/qlist.h index 0078f87cce..443789b411 100644 --- a/src/corelib/tools/qlist.h +++ b/src/corelib/tools/qlist.h @@ -239,6 +239,7 @@ public: void append(const_iterator i1, const_iterator i2); void append(rvalue_ref t) { emplaceBack(std::move(t)); } void append(const QList &l) { append(l.constBegin(), l.constEnd()); } + void append(QList &&l); void prepend(rvalue_ref t); void prepend(const T &t); @@ -434,14 +435,19 @@ public: // comfort QList &operator+=(const QList &l) { append(l.cbegin(), l.cend()); return *this; } + QList &operator+=(QList &&l) { append(std::move(l)); return *this; } inline QList operator+(const QList &l) const { QList n = *this; n += l; return n; } + inline QList operator+(QList &&l) const + { QList n = *this; n += std::move(l); return n; } inline QList &operator+=(const T &t) { append(t); return *this; } inline QList &operator<< (const T &t) { append(t); return *this; } inline QList &operator<<(const QList &l) { *this += l; return *this; } + inline QList &operator<<(QList &&l) + { *this += std::move(l); return *this; } inline QList &operator+=(rvalue_ref t) { append(std::move(t)); return *this; } inline QList &operator<<(rvalue_ref t) @@ -579,6 +585,33 @@ inline void QList::append(const_iterator i1, const_iterator i2) } } +template +inline void QList::append(QList &&other) +{ + if (other.isEmpty()) + return; + if (other.d->needsDetach() || !std::is_nothrow_move_constructible_v) + return append(other); + + const size_t newSize = size() + other.size(); + if (d->needsDetach() || newSize > d->allocatedCapacity()) { + DataPointer detached(Data::allocate(d->detachCapacity(newSize), + d->detachFlags() | Data::GrowsForward)); + + if (!d->needsDetach()) + detached->moveAppend(begin(), end()); + else + detached->copyAppend(cbegin(), cend()); + detached->moveAppend(other.begin(), other.end()); + + d.swap(detached); + } else { + // we're detached and we can just move data around + d->moveAppend(other.begin(), other.end()); + } +} + + template inline typename QList::iterator QList::insert(qsizetype i, qsizetype n, parameter_type t) diff --git a/src/corelib/tools/qlist.qdoc b/src/corelib/tools/qlist.qdoc index 8d1280a2ea..339c089bb8 100644 --- a/src/corelib/tools/qlist.qdoc +++ b/src/corelib/tools/qlist.qdoc @@ -670,6 +670,15 @@ \sa operator<<(), operator+=() */ +/*! \fn template void QList::append(QList &&value) + \overload + + \since 6.0 + + Moves the items of the \a value list to the end of this list. + + \sa operator<<(), operator+=() +*/ /*! \fn template void QList::prepend(const T &value) @@ -1290,6 +1299,14 @@ \sa operator+(), append() */ +/*! \fn template QList &QList::operator+=(QList &&other) + \since 6.0 + + \overload + + \sa operator+(), append() +*/ + /*! \fn template void QList::operator+=(const T &value) \overload @@ -1315,6 +1332,14 @@ \sa operator+=() */ +/*! \fn template QList QList::operator+(QList &&other) const + \since 6.0 + + \overload + + \sa operator+=() +*/ + /*! \fn template QList &QList::operator<<(const T &value) Appends \a value to the list and returns a reference to this list. @@ -1336,6 +1361,12 @@ Appends \a other to the list and returns a reference to the list. */ +/*! \fn template QList &QList::operator<<(QList &&other) + \since 6.0 + + \overload +*/ + /*! \typedef QList::iterator The QList::iterator typedef provides an STL-style non-const diff --git a/tests/auto/corelib/tools/qlist/tst_qlist.cpp b/tests/auto/corelib/tools/qlist/tst_qlist.cpp index 71884a2927..c5ba53ad57 100644 --- a/tests/auto/corelib/tools/qlist/tst_qlist.cpp +++ b/tests/auto/corelib/tools/qlist/tst_qlist.cpp @@ -216,6 +216,7 @@ private slots: void appendMovable() const; void appendCustom() const; void appendRvalue() const; + void appendList() const; void at() const; void capacityInt() const; void capacityMovable() const; @@ -695,6 +696,200 @@ void tst_QList::appendRvalue() const QCOMPARE(v.back(), QString("world")); } +struct ConstructionCounted +{ + ConstructionCounted(int i) : i(i) { } + ConstructionCounted(ConstructionCounted &&other) noexcept + : i(other.i), copies(other.copies), moves(other.moves + 1) + { + // set to some easily noticeable values + other.i = -64; + other.copies = -64; + other.moves = -64; + } + ConstructionCounted &operator=(ConstructionCounted &&other) noexcept + { + i = other.i; + copies = other.copies; + moves = other.moves + 1; + // set to some easily noticeable values + other.i = -64; + other.copies = -64; + other.moves = -64; + return *this; + } + ConstructionCounted(const ConstructionCounted &other) noexcept + : i(other.i), copies(other.copies + 1), moves(other.moves) + { + } + ConstructionCounted &operator=(const ConstructionCounted &other) noexcept + { + i = other.i; + copies = other.copies + 1; + moves = other.moves; + return *this; + } + ~ConstructionCounted() = default; + + friend bool operator==(const ConstructionCounted &lhs, const ConstructionCounted &rhs) + { + return lhs.i == rhs.i; + } + + QString toString() { return QString::number(i); } + + int i; + int copies = 0; + int moves = 0; +}; +QT_BEGIN_NAMESPACE +namespace QTest { +char *toString(const ConstructionCounted &cc) +{ + char *str = new char[5]; + qsnprintf(str, 4, "%d", cc.i); + return str; +} +} +QT_END_NAMESPACE + +void tst_QList::appendList() const +{ + // By const-ref + { + QList v1 = { 1, 2, 3, 4 }; + QList v2 = { 5, 6, 7, 8 }; + v1.append(v2); + QCOMPARE(v2.size(), 4); + QCOMPARE(v1.size(), 8); + QList expected = { 1, 2, 3, 4, 5, 6, 7, 8 }; + QCOMPARE(v1, expected); + + QList doubleExpected = { 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8 }; + // append self to self + v1.append(v1); + QCOMPARE(v1.size(), 16); + QCOMPARE(v1, doubleExpected); + v1.resize(8); + + // append to self, but was shared + QList v1_2(v1); + v1.append(v1); + QCOMPARE(v1_2.size(), 8); + QCOMPARE(v1_2, expected); + QCOMPARE(v1.size(), 16); + QCOMPARE(v1, doubleExpected); + v1.resize(8); + + // append empty + QList v3; + v1.append(v3); + + // append to empty + QList v4; + v4.append(v1); + QCOMPARE(v4, expected); + + v1 = { 1, 2, 3, 4 }; + // Using operators + // << + QList v5; + v5 << v1 << v2; + QCOMPARE(v5, expected); + + // += + QList v6; + v6 += v1; + v6 += v2; + QCOMPARE(v6, expected); + + // + + QCOMPARE(v1 + v2, expected); + } + // By move + { + QList v1 = { 1, 2, 3, 4 }; + // Sanity check + QCOMPARE(v1.at(3).moves, 0); + QCOMPARE(v1.at(3).copies, 1); // because of initializer list + + QList v2 = { 5, 6, 7, 8 }; + v1.append(std::move(v2)); + QCOMPARE(v1.size(), 8); + QList expected = { 1, 2, 3, 4, 5, 6, 7, 8 }; + QCOMPARE(v1, expected); + QCOMPARE(v1.at(0).copies, 1); + QCOMPARE(v1.at(0).moves, 1); + + QCOMPARE(v1.at(4).copies, 1); // was v2.at(0) + QCOMPARE(v1.at(4).moves, 1); + + // append move from empty + QList v3; + v1.append(std::move(v3)); + QCOMPARE(v1.size(), 8); + QCOMPARE(v1, expected); + + for (qsizetype i = 0; i < v1.size(); ++i) { + const auto &counter = v1.at(i); + QCOMPARE(counter.copies, 1); + QCOMPARE(counter.moves, 1); + } + + // append move to empty + QList v4; + v4.reserve(64); + v4.append(std::move(v1)); + QCOMPARE(v4.size(), 8); + QCOMPARE(v4, expected); + + for (qsizetype i = 0; i < v4.size(); ++i) { + const auto &counter = v4.at(i); + QCOMPARE(counter.copies, 1); + QCOMPARE(counter.moves, 2); + } + + QVERIFY(v4.capacity() >= 64); + + v1.swap(v4); // swap back... + + // append move from shared + QList v5 = { 1, 2, 3, 4 }; + QList v5_2(v5); + v1.append(std::move(v5_2)); + QCOMPARE(v1.size(), 12); + QList expectedTwelve = { 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4 }; + QCOMPARE(v1, expectedTwelve); + QCOMPARE(v5.size(), 4); + QList expectedFour = { 1, 2, 3, 4 }; + QCOMPARE(v5, expectedFour); + + QCOMPARE(v5.at(0).copies, 1); // from constructing with std::initializer_list + QCOMPARE(v5.at(0).moves, 0); + + // Using operators + // << + QList v6; + v6 << (QList() << 1 << 2); + v6 << (QList() << 3 << 4); + QCOMPARE(v6, expectedFour); + QCOMPARE(v6.at(0).copies, 2); + QCOMPARE(v6.at(0).moves, 1); + + // += + QList v7; + v7 += (QList() << 1 << 2); + v7 += (QList() << 3 << 4); + QCOMPARE(v7, expectedFour); + + // + + QList v8; + QCOMPARE(v8 + (QList() << 1 << 2 << 3 << 4), expectedFour); + v8 = { 1, 2 }; + QCOMPARE(v8 + (QList() << 3 << 4), expectedFour); + } +} + void tst_QList::at() const { QList myvec;