diff --git a/src/corelib/tools/qarraydataops.h b/src/corelib/tools/qarraydataops.h index 9e5057099e6..fa3824f34b3 100644 --- a/src/corelib/tools/qarraydataops.h +++ b/src/corelib/tools/qarraydataops.h @@ -120,35 +120,6 @@ struct QArrayExceptionSafetyPrimitives } }; - // Watches passed pointer. Unless commit() is called, all the elements that - // the watched iterator passes through are deleted at the end of object - // lifetime. - // - // Requirements: when not at starting position, the iterator is expected to - // point to a valid object (to initialized memory) - struct Destructor - { - T **iter; - T *end; - T *intermediate; - - Destructor(T *&it) noexcept - : iter(std::addressof(it)), end(it) - { } - void commit() noexcept - { - iter = std::addressof(end); - } - ~Destructor() noexcept(std::is_nothrow_destructible_v) - { - // Step is either 1 or -1 and depends on the interposition of *iter - // and end. Note that *iter is expected to point to a valid object - // (see the logic below). - for (const int step = *iter < end ? 1 : -1; *iter != end; std::advance(*iter, step)) - (*iter)->~T(); - } - }; - // Moves the data range in memory by the specified amount. Unless commit() // is called, the data is moved back to the original place at the end of // object lifetime. @@ -218,6 +189,7 @@ struct QPodArrayOps protected: typedef QTypedArrayData Data; + using DataPointer = QArrayDataPointer; public: typedef typename QArrayDataPointer::parameter_type parameter_type; @@ -283,6 +255,23 @@ public: // exception safe; size not updated. } + void insert(qsizetype i, const T *data, qsizetype n) + { + typename Data::GrowthPosition pos = Data::GrowsAtEnd; + if (this->size != 0 && i <= (this->size >> 1)) + pos = Data::GrowsAtBeginning; + DataPointer oldData; + this->detachAndGrow(pos, n, &oldData); + Q_ASSERT((pos == Data::GrowsAtBeginning && this->freeSpaceAtBegin() >= n) || + (pos == Data::GrowsAtEnd && this->freeSpaceAtEnd() >= n)); + + T *where = this->begin() + i; + if (pos == QArrayData::GrowsAtBeginning) + insert(GrowsBackwardsTag{}, where, data, data + n); + else + insert(GrowsForwardTag{}, where, data, data + n); + } + void insert(GrowsForwardTag, T *where, const T *b, const T *e) noexcept { Q_ASSERT(this->isMutable() || (b == e && where == this->end())); @@ -318,6 +307,24 @@ public: this->size += (e - b); } + void insert(qsizetype i, qsizetype n, parameter_type t) + { + T copy(t); + + typename Data::GrowthPosition pos = Data::GrowsAtEnd; + if (this->size != 0 && i <= (this->size >> 1)) + pos = Data::GrowsAtBeginning; + this->detachAndGrow(pos, n); + Q_ASSERT((pos == Data::GrowsAtBeginning && this->freeSpaceAtBegin() >= n) || + (pos == Data::GrowsAtEnd && this->freeSpaceAtEnd() >= n)); + + T *where = this->begin() + i; + if (pos == QArrayData::GrowsAtBeginning) + insert(GrowsBackwardsTag{}, where, n, copy); + else + insert(GrowsForwardTag{}, where, n, copy); + } + void insert(GrowsForwardTag, T *where, size_t n, parameter_type t) noexcept { Q_ASSERT(!this->isShared()); @@ -476,6 +483,7 @@ struct QGenericArrayOps protected: typedef QTypedArrayData Data; + using DataPointer = QArrayDataPointer; public: typedef typename QArrayDataPointer::parameter_type parameter_type; @@ -564,203 +572,179 @@ public: std::destroy(this->begin(), this->end()); } - void insert(GrowsForwardTag, T *where, const T *b, const T *e) + struct Inserter { - Q_ASSERT(this->isMutable() || (b == e && where == this->end())); - Q_ASSERT(!this->isShared() || (b == e && where == this->end())); - Q_ASSERT(where >= this->begin() && where <= this->end()); - Q_ASSERT(b < e); - Q_ASSERT(e <= where || b > this->end() || where == this->end()); // No overlap or append - Q_ASSERT((e - b) <= this->freeSpaceAtEnd()); + QArrayDataPointer *data; + qsizetype increment = 1; + T *begin; + qsizetype size; - using Destructor = typename QArrayExceptionSafetyPrimitives::Destructor; + qsizetype sourceCopyConstruct, nSource, move, sourceCopyAssign; + T *end, *last, *where; - // Array may be truncated at where in case of exceptions - - T *end = this->end(); - T *readIter = end; - T *writeIter = end + (e - b); - - const T *const step1End = where + qMax(e - b, end - where); - - Destructor destroyer(writeIter); - - // Construct new elements in array - while (writeIter != step1End) { - --readIter; - // If exception happens on construction, we should not call ~T() - new (writeIter - 1) T(std::move(*readIter)); - --writeIter; + Inserter(QArrayDataPointer *d, QArrayData::GrowthPosition pos) + : data(d), increment(pos == QArrayData::GrowsAtBeginning ? -1 : 1) + { + begin = d->ptr; + size = d->size; + if (increment < 0) + begin += size - 1; + } + ~Inserter() { + if (increment < 0) + begin -= size - 1; + data->ptr = begin; + data->size = size; } - while (writeIter != end) { - --e; - // If exception happens on construction, we should not call ~T() - new (writeIter - 1) T(*e); - --writeIter; + void setup(qsizetype pos, qsizetype n) + { + + if (increment > 0) { + end = begin + size; + last = end - 1; + where = begin + pos; + qsizetype dist = size - pos; + sourceCopyConstruct = 0; + nSource = n; + move = n - dist; // smaller 0 + sourceCopyAssign = n; + if (n > dist) { + sourceCopyConstruct = n - dist; + move = 0; + sourceCopyAssign -= sourceCopyConstruct; + } + } else { + end = begin - size; + last = end + 1; + where = end + pos; + sourceCopyConstruct = 0; + nSource = -n; + move = pos - n; // larger 0 + sourceCopyAssign = -n; + if (n > pos) { + sourceCopyConstruct = pos - n; + move = 0; + sourceCopyAssign -= sourceCopyConstruct; + } + } } - destroyer.commit(); - this->size += destroyer.end - end; + void insert(qsizetype pos, const T *source, qsizetype n) + { + qsizetype oldSize = size; + Q_UNUSED(oldSize); - // Copy assign over existing elements - while (readIter != where) { - --readIter; - --writeIter; - *writeIter = std::move(*readIter); + setup(pos, n); + if (increment < 0) + source += n - 1; + + // first create new elements at the end, by copying from elements + // to be inserted (if they extend past the current end of the array) + for (qsizetype i = 0; i != sourceCopyConstruct; i += increment) { + new (end + i) T(source[nSource - sourceCopyConstruct + i]); + ++size; + } + Q_ASSERT(size <= oldSize + n); + + // now move construct new elements at the end from existing elements inside + // the array. + for (qsizetype i = sourceCopyConstruct; i != nSource; i += increment) { + new (end + i) T(std::move(*(end + i - nSource))); + ++size; + } + // array has the new size now! + Q_ASSERT(size == oldSize + n); + + // now move assign existing elements towards the end + for (qsizetype i = 0; i != move; i -= increment) + last[i] = std::move(last[i - nSource]); + + // finally copy the remaining elements from source over + for (qsizetype i = 0; i != sourceCopyAssign; i += increment) + where[i] = source[i]; } - while (writeIter != where) { - --e; - --writeIter; - *writeIter = *e; + void insert(qsizetype pos, const T &t, qsizetype n) + { + qsizetype oldSize = size; + Q_UNUSED(oldSize); + + setup(pos, n); + + // first create new elements at the end, by copying from elements + // to be inserted (if they extend past the current end of the array) + for (qsizetype i = 0; i != sourceCopyConstruct; i += increment) { + new (end + i) T(t); + ++size; + } + Q_ASSERT(size <= oldSize + n); + + // now move construct new elements at the end from existing elements inside + // the array. + for (qsizetype i = sourceCopyConstruct; i != nSource; i += increment) { + new (end + i) T(std::move(*(end + i - nSource))); + ++size; + } + // array has the new size now! + Q_ASSERT(size == oldSize + n); + + // now move assign existing elements towards the end + for (qsizetype i = 0; i != move; i -= increment) + last[i] = std::move(last[i - nSource]); + + // finally copy the remaining elements from source over + for (qsizetype i = 0; i != sourceCopyAssign; i += increment) + where[i] = t; } + +#if 0 + void insertHole(T *where) + { + T *oldEnd = end; + + // create a new element at the end by mov constructing one existing element + // inside the array. + new (end) T(std::move(end - increment)); + end += increment; + + // now move existing elements towards the end + T *to = oldEnd; + T *from = oldEnd - increment; + while (from != where) { + *to = std::move(*from); + to -= increment; + from -= increment; + } + } +#endif + }; + + void insert(qsizetype i, const T *data, qsizetype n) + { + typename Data::GrowthPosition pos = Data::GrowsAtEnd; + if (this->size != 0 && i <= (this->size >> 1)) + pos = Data::GrowsAtBeginning; + DataPointer oldData; + this->detachAndGrow(pos, n, &oldData); + Q_ASSERT((pos == Data::GrowsAtBeginning && this->freeSpaceAtBegin() >= n) || + (pos == Data::GrowsAtEnd && this->freeSpaceAtEnd() >= n)); + + Inserter(this, pos).insert(i, data, n); } - void insert(GrowsBackwardsTag, T *where, const T *b, const T *e) + void insert(qsizetype i, qsizetype n, parameter_type t) { - Q_ASSERT(this->isMutable() || (b == e && where == this->end())); - Q_ASSERT(!this->isShared() || (b == e && where == this->end())); - Q_ASSERT(where >= this->begin() && where <= this->end()); - Q_ASSERT(b < e); - Q_ASSERT(e <= where || b > this->end() || where == this->end()); // No overlap or append - Q_ASSERT((e - b) <= this->freeSpaceAtBegin()); + T copy(t); - using Destructor = typename QArrayExceptionSafetyPrimitives::Destructor; + typename Data::GrowthPosition pos = Data::GrowsAtEnd; + if (this->size != 0 && i <= (this->size >> 1)) + pos = Data::GrowsAtBeginning; + this->detachAndGrow(pos, n); + Q_ASSERT((pos == Data::GrowsAtBeginning && this->freeSpaceAtBegin() >= n) || + (pos == Data::GrowsAtEnd && this->freeSpaceAtEnd() >= n)); - T *begin = this->begin(); - T *readIter = begin; - T *writeIter = begin - (e - b); - - const T *const step1End = where - qMax(e - b, where - begin); - - Destructor destroyer(writeIter); - - // Construct new elements in array - while (writeIter != step1End) { - new (writeIter) T(std::move(*readIter)); - ++readIter; - ++writeIter; - } - - while (writeIter != begin) { - new (writeIter) T(*b); - ++b; - ++writeIter; - } - - destroyer.commit(); - this->size += begin - destroyer.end; - this->ptr -= begin - destroyer.end; - - // Copy assign over existing elements - while (readIter != where) { - *writeIter = std::move(*readIter); - ++readIter; - ++writeIter; - } - - while (writeIter != where) { - *writeIter = *b; - ++b; - ++writeIter; - } - } - - void insert(GrowsForwardTag, T *where, size_t n, parameter_type t) - { - Q_ASSERT(!this->isShared()); - Q_ASSERT(n); - Q_ASSERT(where >= this->begin() && where <= this->end()); - Q_ASSERT(size_t(this->freeSpaceAtEnd()) >= n); - - using Destructor = typename QArrayExceptionSafetyPrimitives::Destructor; - - // Array may be truncated at where in case of exceptions - T *end = this->end(); - T *readIter = end; - T *writeIter = end + n; - - const T *const step1End = where + qMax(n, end - where); - - Destructor destroyer(writeIter); - - // Construct new elements in array - while (writeIter != step1End) { - --readIter; - // If exception happens on construction, we should not call ~T() - new (writeIter - 1) T(std::move(*readIter)); - --writeIter; - } - - while (writeIter != end) { - --n; - // If exception happens on construction, we should not call ~T() - new (writeIter - 1) T(t); - --writeIter; - } - - destroyer.commit(); - this->size += destroyer.end - end; - - // Copy assign over existing elements - while (readIter != where) { - --readIter; - --writeIter; - *writeIter = std::move(*readIter); - } - - while (writeIter != where) { - --n; - --writeIter; - *writeIter = t; - } - } - - void insert(GrowsBackwardsTag, T *where, size_t n, parameter_type t) - { - Q_ASSERT(!this->isShared()); - Q_ASSERT(n); - Q_ASSERT(where >= this->begin() && where <= this->end()); - Q_ASSERT(size_t(this->freeSpaceAtBegin()) >= n); - - using Destructor = typename QArrayExceptionSafetyPrimitives::Destructor; - - T *begin = this->begin(); - T *readIter = begin; - T *writeIter = begin - n; - - const T *const step1End = where - qMax(n, where - begin); - - Destructor destroyer(writeIter); - - // Construct new elements in array - while (writeIter != step1End) { - new (writeIter) T(std::move(*readIter)); - ++readIter; - ++writeIter; - } - - while (writeIter != begin) { - new (writeIter) T(t); - ++writeIter; - } - - destroyer.commit(); - this->size += begin - destroyer.end; - this->ptr -= begin - destroyer.end; - - // Copy assign over existing elements - while (readIter != where) { - *writeIter = std::move(*readIter); - ++readIter; - ++writeIter; - } - - while (writeIter != where) { - *writeIter = t; - ++writeIter; - } + Inserter(this, pos).insert(i, copy, n); } template @@ -913,6 +897,7 @@ struct QMovableArrayOps protected: typedef QTypedArrayData Data; + using DataPointer = QArrayDataPointer; public: // using QGenericArrayOps::appendInitialize; @@ -922,6 +907,23 @@ public: // using QGenericArrayOps::destroyAll; typedef typename QGenericArrayOps::parameter_type parameter_type; + void insert(qsizetype i, const T *data, qsizetype n) + { + typename Data::GrowthPosition pos = Data::GrowsAtEnd; + if (this->size != 0 && i <= (this->size >> 1)) + pos = Data::GrowsAtBeginning; + DataPointer oldData; + this->detachAndGrow(pos, n, &oldData); + Q_ASSERT((pos == Data::GrowsAtBeginning && this->freeSpaceAtBegin() >= n) || + (pos == Data::GrowsAtEnd && this->freeSpaceAtEnd() >= n)); + + T *where = this->begin() + i; + if (pos == QArrayData::GrowsAtBeginning) + insert(GrowsBackwardsTag{}, where, data, data + n); + else + insert(GrowsForwardTag{}, where, data, data + n); + } + void insert(GrowsForwardTag, T *where, const T *b, const T *e) { Q_ASSERT(this->isMutable() || (b == e && where == this->end())); @@ -967,6 +969,24 @@ public: this->size += copiedSize; } + void insert(qsizetype i, qsizetype n, parameter_type t) + { + T copy(t); + + typename Data::GrowthPosition pos = Data::GrowsAtEnd; + if (this->size != 0 && i <= (this->size >> 1)) + pos = Data::GrowsAtBeginning; + this->detachAndGrow(pos, n); + Q_ASSERT((pos == Data::GrowsAtBeginning && this->freeSpaceAtBegin() >= n) || + (pos == Data::GrowsAtEnd && this->freeSpaceAtEnd() >= n)); + + T *where = this->begin() + i; + if (pos == QArrayData::GrowsAtBeginning) + insert(GrowsBackwardsTag{}, where, n, copy); + else + insert(GrowsForwardTag{}, where, n, copy); + } + void insert(GrowsForwardTag, T *where, size_t n, parameter_type t) { Q_ASSERT(!this->isShared()); @@ -1169,41 +1189,6 @@ public: } public: - void insert(qsizetype i, qsizetype n, parameter_type t) - { - T copy(t); - - typename Data::GrowthPosition pos = Data::GrowsAtEnd; - if (this->size != 0 && i <= (this->size >> 1)) - pos = Data::GrowsAtBeginning; - this->detachAndGrow(pos, n); - Q_ASSERT((pos == Data::GrowsAtBeginning && this->freeSpaceAtBegin() >= n) || - (pos == Data::GrowsAtEnd && this->freeSpaceAtEnd() >= n)); - - T *where = this->begin() + i; - if (pos == QArrayData::GrowsAtBeginning) - Base::insert(GrowsBackwardsTag{}, where, n, copy); - else - Base::insert(GrowsForwardTag{}, where, n, copy); - } - - void insert(qsizetype i, const T *data, qsizetype n) - { - typename Data::GrowthPosition pos = Data::GrowsAtEnd; - if (this->size != 0 && i <= (this->size >> 1)) - pos = Data::GrowsAtBeginning; - DataPointer oldData; - this->detachAndGrow(pos, n, &oldData); - Q_ASSERT((pos == Data::GrowsAtBeginning && this->freeSpaceAtBegin() >= n) || - (pos == Data::GrowsAtEnd && this->freeSpaceAtEnd() >= n)); - - T *where = this->begin() + i; - if (pos == QArrayData::GrowsAtBeginning) - Base::insert(GrowsBackwardsTag{}, where, data, data + n); - else - Base::insert(GrowsForwardTag{}, where, data, data + n); - } - template void emplace(qsizetype i, Args &&... args) { diff --git a/src/corelib/tools/qarraydatapointer.h b/src/corelib/tools/qarraydatapointer.h index 1aa9a670e73..763a5f79270 100644 --- a/src/corelib/tools/qarraydatapointer.h +++ b/src/corelib/tools/qarraydatapointer.h @@ -282,11 +282,8 @@ public: return lhs.data() != rhs.data() || lhs.size != rhs.size; } -protected: Data *d; T *ptr; - -public: qsizetype size; }; diff --git a/tests/auto/corelib/tools/collections/tst_collections.cpp b/tests/auto/corelib/tools/collections/tst_collections.cpp index 0d4623f7fdf..4430de7a7dc 100644 --- a/tests/auto/corelib/tools/collections/tst_collections.cpp +++ b/tests/auto/corelib/tools/collections/tst_collections.cpp @@ -3329,6 +3329,18 @@ template void insert_remove_loop_impl() for (int i = 0; i < t.count(); i++) { QCOMPARE(t[i], T(IntOrString(expect5[i]))); } + + t.clear(); + t << T(IntOrString(1)) << T(IntOrString(2)) << T(IntOrString(3)) << T(IntOrString(4)); + t.insert(2, 4, T(IntOrString(9))); + t.insert(2, 4, T(IntOrString(7))); + + int expect6[] = { 1, 2, 7, 7, 7, 7, 9, 9, 9, 9, 3, 4 }; + QCOMPARE(size_t(t.count()), sizeof(expect6)/sizeof(int)); + for (int i = 0; i < t.count(); i++) { + QCOMPARE(t[i], T(IntOrString(expect6[i]))); + } + } diff --git a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp index f39389fa60e..d9a964ee96d 100644 --- a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp +++ b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp @@ -86,7 +86,6 @@ private slots: void selfEmplaceForward(); #ifndef QT_NO_EXCEPTIONS void exceptionSafetyPrimitives_constructor(); - void exceptionSafetyPrimitives_destructor(); void exceptionSafetyPrimitives_mover(); void exceptionSafetyPrimitives_displacer(); #endif @@ -2507,183 +2506,6 @@ void tst_QArrayData::exceptionSafetyPrimitives_constructor() } } -void tst_QArrayData::exceptionSafetyPrimitives_destructor() -{ - using Prims = QtPrivate::QArrayExceptionSafetyPrimitives; - using Destructor = typename Prims::Destructor; - - struct WatcherScope - { - WatcherScope() { throwingTypeWatcher().watch = true; } - ~WatcherScope() - { - throwingTypeWatcher().watch = false; - throwingTypeWatcher().destroyedIds.clear(); - } - }; - - // successful operation with no rollback, elements added from left to right - { - auto data = createDataPointer(20, 10); - auto reference = createDataPointer(20, 10); - reference->insert(reference.size, 2, ThrowingType(42)); - - WatcherScope scope; Q_UNUSED(scope); - { - ThrowingType *where = data.end() - 1; - Destructor destroyer(where); - for (int i = 0; i < 2; ++i) { - new (where + 1) ThrowingType(42); - ++where; - ++data.size; - } - destroyer.commit(); - } - - QCOMPARE(data.size, reference.size); - for (qsizetype i = 0; i < data.size; ++i) - QCOMPARE(data.data()[i], reference.data()[i]); - QVERIFY(throwingTypeWatcher().destroyedIds.size() == 0); - } - - // failed operation with rollback, elements added from left to right - { - auto data = createDataPointer(20, 10); - auto reference = createDataPointer(20, 10); - - WatcherScope scope; Q_UNUSED(scope); - try { - ThrowingType *where = data.end() - 1; - Destructor destroyer(where); - for (int i = 0; i < 2; ++i) { - new (where + 1) ThrowingType(42 + i); - ++where; - ThrowingType::throwOnce = 1; - } - QFAIL("Unreachable line!"); - destroyer.commit(); - } catch (const std::runtime_error &e) { - QCOMPARE(std::string(e.what()), ThrowingType::throwString); - QCOMPARE(data.size, reference.size); - for (qsizetype i = 0; i < data.size; ++i) - QCOMPARE(data.data()[i], reference.data()[i]); - QVERIFY(throwingTypeWatcher().destroyedIds.size() == 1); - QCOMPARE(throwingTypeWatcher().destroyedIds[0], 42); - } - } - - // successful operation with no rollback, elements added from right to left - { - auto data = createDataPointer(20, 10); - auto reference = createDataPointer(20, 10); - reference->erase(reference.begin(), reference.begin() + 2); - reference->insert(0, 2, ThrowingType(42)); - - data.begin()->~ThrowingType(); - data.begin()->~ThrowingType(); - data.size -= 2; - WatcherScope scope; Q_UNUSED(scope); - { - ThrowingType *where = data.begin() + 2; // Note: not updated data ptr, so begin + 2 - Destructor destroyer(where); - for (int i = 0; i < 2; ++i) { - new (where - 1) ThrowingType(42); - --where; - ++data.size; - } - destroyer.commit(); - } - QCOMPARE(data.size, reference.size); - for (qsizetype i = 0; i < data.size; ++i) - QCOMPARE(data.data()[i], reference.data()[i]); - QVERIFY(throwingTypeWatcher().destroyedIds.size() == 0); - } - - // failed operation with rollback, elements added from right to left - { - auto data = createDataPointer(20, 10); - auto reference = createDataPointer(20, 10); - reference->erase(reference.begin(), reference.begin() + 2); - - data.begin()->~ThrowingType(); - data.begin()->~ThrowingType(); - data.size -= 2; - WatcherScope scope; Q_UNUSED(scope); - try { - ThrowingType *where = data.begin() + 2; // Note: not updated data ptr, so begin + 2 - Destructor destroyer(where); - for (int i = 0; i < 2; ++i) { - new (where - 1) ThrowingType(42 + i); - --where; - ThrowingType::throwOnce = 1; - } - QFAIL("Unreachable line!"); - destroyer.commit(); - } catch (const std::runtime_error &e) { - QCOMPARE(std::string(e.what()), ThrowingType::throwString); - QCOMPARE(data.size, reference.size); - for (qsizetype i = 0; i < data.size; ++i) - QCOMPARE(data.data()[i + 2], reference.data()[i]); - QVERIFY(throwingTypeWatcher().destroyedIds.size() == 1); - QCOMPARE(throwingTypeWatcher().destroyedIds[0], 42); - } - } - - // extra: the very first operation throws - destructor has to do nothing, - // since nothing is properly constructed - { - auto data = createDataPointer(20, 10); - auto reference = createDataPointer(20, 10); - - WatcherScope scope; Q_UNUSED(scope); - try { - ThrowingType *where = data.end() - 1; - Destructor destroyer(where); - ThrowingType::throwOnce = 1; - new (where + 1) ThrowingType(42); - ++where; - QFAIL("Unreachable line!"); - destroyer.commit(); - } catch (const std::runtime_error &e) { - QCOMPARE(data.size, reference.size); - for (qsizetype i = 0; i < data.size; ++i) - QCOMPARE(data.data()[i], reference.data()[i]); - QVERIFY(throwingTypeWatcher().destroyedIds.size() == 0); - } - } - - // extra: special case when iterator is intentionally out of bounds: this is - // to cover the case when we work on the uninitialized memory region instead - // of being near the border - { - auto data = createDataPointer(20, 10); - auto reference = createDataPointer(20, 10); - reference->erase(reference.begin(), reference.begin() + 2); - - data.begin()->~ThrowingType(); - data.begin()->~ThrowingType(); - data.size -= 2; - WatcherScope scope; Q_UNUSED(scope); - try { - ThrowingType *where = data.begin() - 1; // Note: intentionally out of range - Destructor destroyer(where); - for (int i = 0; i < 2; ++i) { - new (where + 1) ThrowingType(42); - ++where; - ThrowingType::throwOnce = 1; - } - QFAIL("Unreachable line!"); - destroyer.commit(); - } catch (const std::runtime_error &e) { - QCOMPARE(data.size, reference.size); - for (qsizetype i = 0; i < data.size; ++i) - QCOMPARE(data.data()[i + 2], reference.data()[i]); - QVERIFY(throwingTypeWatcher().destroyedIds.size() == 1); - QVERIFY(throwingTypeWatcher().destroyedIds[0] == 42); - } - } -} - void tst_QArrayData::exceptionSafetyPrimitives_mover() { QVERIFY(QTypeInfo::isRelocatable);