diff --git a/src/corelib/tools/qvarlengtharray.h b/src/corelib/tools/qvarlengtharray.h index 90311ac55b..b2f05452ae 100644 --- a/src/corelib/tools/qvarlengtharray.h +++ b/src/corelib/tools/qvarlengtharray.h @@ -178,7 +178,9 @@ public: inline QVarLengthArray &operator+=(const T &t) { append(t); return *this; } + void prepend(T &&t); void prepend(const T &t); + void insert(int i, T &&t); void insert(int i, const T &t); void insert(int i, int n, const T &t); void replace(int i, const T &t); @@ -218,6 +220,7 @@ public: const_reverse_iterator crbegin() const { return const_reverse_iterator(end()); } const_reverse_iterator crend() const { return const_reverse_iterator(begin()); } iterator insert(const_iterator before, int n, const T &x); + iterator insert(const_iterator before, T &&x); inline iterator insert(const_iterator before, const T &x) { return insert(before, 1, x); } iterator erase(const_iterator begin, const_iterator end); inline iterator erase(const_iterator pos) { return erase(pos, pos+1); } @@ -373,9 +376,9 @@ Q_OUTOFLINE_TEMPLATE void QVarLengthArray::realloc(int asize, int a s = 0; if (!QTypeInfoQuery::isRelocatable) { QT_TRY { - // copy all the old elements + // move all the old elements while (s < copySize) { - new (ptr+s) T(*(oldPtr+s)); + new (ptr+s) T(std::move(*(oldPtr+s))); (oldPtr+s)->~T(); s++; } @@ -426,6 +429,10 @@ Q_OUTOFLINE_TEMPLATE T QVarLengthArray::value(int i, const T &defau return (uint(i) >= uint(size())) ? defaultValue : at(i); } +template +inline void QVarLengthArray::insert(int i, T &&t) +{ Q_ASSERT_X(i >= 0 && i <= s, "QVarLengthArray::insert", "index out of range"); + insert(cbegin() + i, std::move(t)); } template inline void QVarLengthArray::insert(int i, const T &t) { Q_ASSERT_X(i >= 0 && i <= s, "QVarLengthArray::insert", "index out of range"); @@ -443,6 +450,9 @@ inline void QVarLengthArray::remove(int i) { Q_ASSERT_X(i >= 0 && i < s, "QVarLengthArray::remove", "index out of range"); erase(begin() + i, begin() + i + 1); } template +inline void QVarLengthArray::prepend(T &&t) +{ insert(cbegin(), std::move(t)); } +template inline void QVarLengthArray::prepend(const T &t) { insert(begin(), 1, t); } @@ -454,6 +464,34 @@ inline void QVarLengthArray::replace(int i, const T &t) data()[i] = copy; } +template +Q_OUTOFLINE_TEMPLATE typename QVarLengthArray::iterator QVarLengthArray::insert(const_iterator before, T &&t) +{ + Q_ASSERT_X(isValidIterator(before), "QVarLengthArray::insert", "The specified const_iterator argument 'before' is invalid"); + + int offset = int(before - ptr); + reserve(s + 1); + if (!QTypeInfo::isRelocatable) { + T *b = ptr + offset; + T *i = ptr + s; + T *j = i + 1; + // The new end-element needs to be constructed, the rest must be move assigned + if (i != b) { + new (--j) T(std::move(*--i)); + while (i != b) + *--j = std::move(*--i); + *b = std::move(t); + } else { + new (b) T(std::move(t)); + } + } else { + T *b = ptr + offset; + memmove(b + 1, b, (s - offset) * sizeof(T)); + new (b) T(std::move(t)); + } + s += 1; + return ptr + offset; +} template Q_OUTOFLINE_TEMPLATE typename QVarLengthArray::iterator QVarLengthArray::insert(const_iterator before, size_type n, const T &t) diff --git a/src/corelib/tools/qvector.h b/src/corelib/tools/qvector.h index ce6e657be0..5ebf7f7435 100644 --- a/src/corelib/tools/qvector.h +++ b/src/corelib/tools/qvector.h @@ -137,7 +137,9 @@ public: void append(T &&t); #endif inline void append(const QVector &l) { *this += l; } + void prepend(T &&t); void prepend(const T &t); + void insert(int i, T &&t); void insert(int i, const T &t); void insert(int i, int n, const T &t); void replace(int i, const T &t); @@ -226,6 +228,7 @@ public: const_reverse_iterator crend() const Q_DECL_NOTHROW { return const_reverse_iterator(begin()); } iterator insert(iterator before, int n, const T &x); inline iterator insert(iterator before, const T &x) { return insert(before, 1, x); } + inline iterator insert(iterator before, T &&x); iterator erase(iterator begin, iterator end); inline iterator erase(iterator pos) { return erase(pos, pos+1); } @@ -257,6 +260,7 @@ public: inline void push_back(const T &t) { append(t); } #ifdef Q_COMPILER_RVALUE_REFS void push_back(T &&t) { append(std::move(t)); } + void push_front(T &&t) { prepend(std::move(t)); } #endif inline void push_front(const T &t) { prepend(t); } void pop_back() { removeLast(); } @@ -436,6 +440,10 @@ inline void QVector::insert(int i, int n, const T &t) { Q_ASSERT_X(i >= 0 && i <= d->size, "QVector::insert", "index out of range"); insert(begin() + i, n, t); } template +inline void QVector::insert(int i, T &&t) +{ Q_ASSERT_X(i >= 0 && i <= d->size, "QVector::insert", "index out of range"); + insert(begin() + i, std::move(t)); } +template inline void QVector::remove(int i, int n) { Q_ASSERT_X(i >= 0 && n >= 0 && i + n <= d->size, "QVector::remove", "index out of range"); erase(d->begin() + i, d->begin() + i + n); } @@ -446,6 +454,9 @@ inline void QVector::remove(int i) template inline void QVector::prepend(const T &t) { insert(begin(), 1, t); } +template +inline void QVector::prepend(T &&t) +{ insert(begin(), std::move(t)); } template inline void QVector::replace(int i, const T &t) @@ -558,9 +569,19 @@ void QVector::reallocData(const int asize, const int aalloc, QArrayData::Allo T *dst = x->begin(); if (!QTypeInfoQuery::isRelocatable || (isShared && QTypeInfo::isComplex)) { - // we can not move the data, we need to copy construct it - while (srcBegin != srcEnd) { - new (dst++) T(*srcBegin++); + QT_TRY { + if (isShared || !std::is_nothrow_move_constructible::value) { + // we can not move the data, we need to copy construct it + while (srcBegin != srcEnd) + new (dst++) T(*srcBegin++); + } else { + while (srcBegin != srcEnd) + new (dst++) T(std::move(*srcBegin++)); + } + } QT_CATCH (...) { + // destruct already copied objects + destruct(x->begin(), dst); + QT_RETHROW; } } else { ::memcpy(static_cast(dst), static_cast(srcBegin), (srcEnd - srcBegin) * sizeof(T)); @@ -573,12 +594,17 @@ void QVector::reallocData(const int asize, const int aalloc, QArrayData::Allo if (asize > d->size) { // construct all new objects when growing - QT_TRY { - defaultConstruct(dst, x->end()); - } QT_CATCH (...) { - // destruct already copied objects - destruct(x->begin(), dst); - QT_RETHROW; + if (!QTypeInfo::isComplex) { + ::memset(static_cast(dst), 0, (static_cast(x->end()) - dst) * sizeof(T)); + } else { + QT_TRY { + while (dst != x->end()) + new (dst++) T(); + } QT_CATCH (...) { + // destruct already copied objects + destruct(x->begin(), dst); + QT_RETHROW; + } } } } QT_CATCH (...) { @@ -730,6 +756,36 @@ typename QVector::iterator QVector::insert(iterator before, size_type n, c return d->begin() + offset; } +template +typename QVector::iterator QVector::insert(iterator before, T &&t) +{ + Q_ASSERT_X(isValidIterator(before), "QVector::insert", "The specified iterator argument 'before' is invalid"); + + const auto offset = std::distance(d->begin(), before); + if (!isDetached() || d->size + 1 > int(d->alloc)) + reallocData(d->size, d->size + 1, QArrayData::Grow); + if (!QTypeInfoQuery::isRelocatable) { + T *i = d->end(); + T *j = i + 1; + T *b = d->begin() + offset; + // The new end-element needs to be constructed, the rest must be move assigned + if (i != b) { + new (--j) T(std::move(*--i)); + while (i != b) + *--j = std::move(*--i); + *b = std::move(t); + } else { + new (b) T(std::move(t)); + } + } else { + T *b = d->begin() + offset; + memmove(b + 1, b, (d->size - offset) * sizeof(T)); + new (b) T(std::move(t)); + } + d->size += 1; + return d->begin() + offset; +} + template typename QVector::iterator QVector::erase(iterator abegin, iterator aend) { diff --git a/tests/auto/corelib/tools/qvarlengtharray/tst_qvarlengtharray.cpp b/tests/auto/corelib/tools/qvarlengtharray/tst_qvarlengtharray.cpp index 3971353cbb..229969a943 100644 --- a/tests/auto/corelib/tools/qvarlengtharray/tst_qvarlengtharray.cpp +++ b/tests/auto/corelib/tools/qvarlengtharray/tst_qvarlengtharray.cpp @@ -53,6 +53,9 @@ private slots: void initializeListInt(); void initializeListMovable(); void initializeListComplex(); + void insertMove(); + void nonCopyable(); + private: template void initializeList(); @@ -287,6 +290,11 @@ struct MyBase } else { ++errorCount; } + if (!data) { + --movedCount; + ++liveCount; + } + data = this; return *this; } @@ -294,36 +302,46 @@ struct MyBase ~MyBase() { if (isCopy) { - if (!copyCount) + if (!copyCount || !data) ++errorCount; else --copyCount; } - if (!liveCount) - ++errorCount; - else - --liveCount; + if (data) { + if (!liveCount) + ++errorCount; + else + --liveCount; + } else + --movedCount; } - bool hasMoved() const + bool wasConstructedAt(const MyBase *that) const { - return this != data; + return that == data; } + bool hasMoved() const { return !wasConstructedAt(this); } + protected: - MyBase const * const data; + MyBase(const MyBase *data, bool isCopy) + : data(data), isCopy(isCopy) {} + + const MyBase *data; bool isCopy; public: static int errorCount; static int liveCount; static int copyCount; + static int movedCount; }; int MyBase::errorCount = 0; int MyBase::liveCount = 0; int MyBase::copyCount = 0; +int MyBase::movedCount = 0; struct MyPrimitive : MyBase @@ -348,7 +366,39 @@ struct MyPrimitive struct MyMovable : MyBase { - MyMovable(char input = 'j') : i(input) {} + MyMovable(char input = 'j') : MyBase(), i(input) {} + + MyMovable(MyMovable const &other) : MyBase(other), i(other.i) {} + + MyMovable(MyMovable &&other) : MyBase(other.data, other.isCopy), i(other.i) + { + ++movedCount; + other.isCopy = false; + other.data = nullptr; + } + + MyMovable & operator=(const MyMovable &other) + { + MyBase::operator=(other); + i = other.i; + return *this; + } + + MyMovable & operator=(MyMovable &&other) + { + if (isCopy) + --copyCount; + ++movedCount; + if (other.data) + --liveCount; + isCopy = other.isCopy; + data = other.data; + other.isCopy = false; + other.data = nullptr; + + return *this; + } + bool operator==(const MyMovable &other) const { return i == other.i; @@ -898,5 +948,83 @@ void tst_QVarLengthArray::initializeList() #endif } +void tst_QVarLengthArray::insertMove() +{ + MyBase::errorCount = 0; + QCOMPARE(MyBase::liveCount, 0); + QCOMPARE(MyBase::copyCount, 0); + + { + QVarLengthArray vec; + MyMovable m1; + MyMovable m2; + MyMovable m3; + MyMovable m4; + QCOMPARE(MyBase::copyCount, 0); + QCOMPARE(MyBase::liveCount, 4); + + vec.append(std::move(m3)); + QVERIFY(vec.at(0).wasConstructedAt(&m3)); + QCOMPARE(MyBase::errorCount, 0); + QCOMPARE(MyBase::liveCount, 4); + QCOMPARE(MyBase::movedCount, 1); + + vec.push_back(std::move(m4)); + QVERIFY(vec.at(0).wasConstructedAt(&m3)); + QVERIFY(vec.at(1).wasConstructedAt(&m4)); + QCOMPARE(MyBase::errorCount, 0); + QCOMPARE(MyBase::liveCount, 4); + QCOMPARE(MyBase::movedCount, 2); + + vec.prepend(std::move(m1)); + QVERIFY(vec.at(0).wasConstructedAt(&m1)); + QVERIFY(vec.at(1).wasConstructedAt(&m3)); + QVERIFY(vec.at(2).wasConstructedAt(&m4)); + QCOMPARE(MyBase::errorCount, 0); + QCOMPARE(MyBase::liveCount, 4); + QCOMPARE(MyBase::movedCount, 3); + + vec.insert(1, std::move(m2)); + QVERIFY(vec.at(0).wasConstructedAt(&m1)); + QVERIFY(vec.at(1).wasConstructedAt(&m2)); + QVERIFY(vec.at(2).wasConstructedAt(&m3)); + + QCOMPARE(MyBase::copyCount, 0); + QCOMPARE(MyBase::liveCount, 4); + QCOMPARE(MyBase::errorCount, 0); + QCOMPARE(MyBase::movedCount, 4); + } + QCOMPARE(MyBase::liveCount, 0); + QCOMPARE(MyBase::errorCount, 0); + QCOMPARE(MyBase::movedCount, 0); +} + +void tst_QVarLengthArray::nonCopyable() +{ + QVarLengthArray> vec; + std::unique_ptr val1(new int(1)); + std::unique_ptr val2(new int(2)); + std::unique_ptr val3(new int(3)); + std::unique_ptr val4(new int(4)); + int *const ptr1 = val1.get(); + int *const ptr2 = val2.get(); + int *const ptr3 = val3.get(); + int *const ptr4 = val4.get(); + + vec.append(std::move(val3)); + QVERIFY(ptr3 == vec.at(0).get()); + vec.append(std::move(val4)); + QVERIFY(ptr3 == vec.at(0).get()); + QVERIFY(ptr4 == vec.at(1).get()); + vec.prepend(std::move(val1)); + QVERIFY(ptr1 == vec.at(0).get()); + QVERIFY(ptr3 == vec.at(1).get()); + QVERIFY(ptr4 == vec.at(2).get()); + vec.insert(1, std::move(val2)); + QVERIFY(ptr1 == vec.at(0).get()); + QVERIFY(ptr2 == vec.at(1).get()); + QVERIFY(ptr3 == vec.at(2).get()); +} + QTEST_APPLESS_MAIN(tst_QVarLengthArray) #include "tst_qvarlengtharray.moc" diff --git a/tests/auto/corelib/tools/qvector/tst_qvector.cpp b/tests/auto/corelib/tools/qvector/tst_qvector.cpp index 713109a214..ef10357b6d 100644 --- a/tests/auto/corelib/tools/qvector/tst_qvector.cpp +++ b/tests/auto/corelib/tools/qvector/tst_qvector.cpp @@ -35,17 +35,28 @@ struct Movable { Movable(char input = 'j') : i(input) + , that(this) , state(Constructed) { counter.fetchAndAddRelaxed(1); } Movable(const Movable &other) : i(other.i) + , that(this) , state(Constructed) { check(other.state, Constructed); counter.fetchAndAddRelaxed(1); } + Movable(Movable &&other) + : i(other.i) + , that(other.that) + , state(Constructed) + { + check(other.state, Constructed); + counter.fetchAndAddRelaxed(1); + other.that = nullptr; + } ~Movable() { @@ -67,11 +78,27 @@ struct Movable { check(state, Constructed); check(other.state, Constructed); i = other.i; + that = this; return *this; } + Movable &operator=(Movable &&other) + { + check(state, Constructed); + check(other.state, Constructed); + i = other.i; + that = other.that; + other.that = nullptr; + return *this; + } + bool wasConstructedAt(const Movable *other) const + { + return that == other; + } char i; static QAtomicInt counter; private: + Movable *that; // used to check if an instance was moved + enum State { Constructed = 106, Destructed = 110 }; State state; @@ -297,6 +324,8 @@ private slots: void detachThreadSafetyMovable() const; void detachThreadSafetyCustom() const; + void insertMove() const; + private: template void copyConstructor() const; template void add() const; @@ -2861,6 +2890,34 @@ void tst_QVector::detachThreadSafetyCustom() const } } +void tst_QVector::insertMove() const +{ + const int instancesCount = Movable::counter.loadAcquire(); + { + QVector vec; + Movable m1; + Movable m2; + Movable m3; + Movable m4; + + vec.append(std::move(m3)); + QVERIFY(vec.at(0).wasConstructedAt(&m3)); + vec.push_back(std::move(m4)); + QVERIFY(vec.at(0).wasConstructedAt(&m3)); + QVERIFY(vec.at(1).wasConstructedAt(&m4)); + vec.prepend(std::move(m1)); + QVERIFY(vec.at(0).wasConstructedAt(&m1)); + QVERIFY(vec.at(1).wasConstructedAt(&m3)); + QVERIFY(vec.at(2).wasConstructedAt(&m4)); + vec.insert(1, std::move(m2)); + QVERIFY(vec.at(0).wasConstructedAt(&m1)); + QVERIFY(vec.at(1).wasConstructedAt(&m2)); + QVERIFY(vec.at(2).wasConstructedAt(&m3)); + + QCOMPARE(Movable::counter.loadAcquire(), instancesCount + 8); + } + QCOMPARE(Movable::counter.loadAcquire(), instancesCount); +} QTEST_MAIN(tst_QVector) #include "tst_qvector.moc"