Cleanup QArrayDataOps::erase()

Move the implementation into the different specializations, and
simplify the implementation. This can be done since we know
that destructors will not throw exceptions for types stored in
our containers.

Change-Id: I9556cd384ef99a623b5b8723b65f16eb9e1df767
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Reviewed-by: Andrei Golubev <andrei.golubev@qt.io>
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
This commit is contained in:
Lars Knoll 2020-11-09 16:23:55 +01:00
parent 621c05e3b1
commit d3db51ef4a

View File

@ -381,32 +381,24 @@ public:
++this->size;
}
void erase(GrowsForwardTag, T *b, T *e) noexcept
void erase(T *b, T *e)
{
Q_ASSERT(this->isMutable());
Q_ASSERT(b < e);
Q_ASSERT(b >= this->begin() && b < this->end());
Q_ASSERT(e > this->begin() && e <= this->end());
if (e != this->end())
// Comply with std::vector::erase(): erased elements and all after them
// are invalidated. However, erasing from the beginning effectively
// means that all iterators are invalidated. We can use this freedom to
// erase by moving towards the end.
if (b == this->begin())
this->ptr = e;
else if (e != this->end())
::memmove(static_cast<void *>(b), static_cast<void *>(e), (static_cast<T *>(this->end()) - e) * sizeof(T));
this->size -= (e - b);
}
void erase(GrowsBackwardsTag, T *b, T *e) noexcept
{
Q_ASSERT(this->isMutable());
Q_ASSERT(b < e);
Q_ASSERT(b >= this->begin() && b < this->end());
Q_ASSERT(e > this->begin() && e <= this->end());
const auto oldBegin = this->begin();
this->ptr += (e - b);
if (b != oldBegin)
::memmove(static_cast<void *>(this->begin()), static_cast<void *>(oldBegin), (b - static_cast<T *>(oldBegin)) * sizeof(T));
this->size -= (e - b);
}
void eraseFirst() noexcept
{
Q_ASSERT(this->size);
@ -799,13 +791,20 @@ public:
}
}
void erase(GrowsForwardTag, T *b, T *e)
void erase(T *b, T *e)
{
Q_ASSERT(this->isMutable());
Q_ASSERT(b < e);
Q_ASSERT(b >= this->begin() && b < this->end());
Q_ASSERT(e > this->begin() && e <= this->end());
// Comply with std::vector::erase(): erased elements and all after them
// are invalidated. However, erasing from the beginning effectively
// means that all iterators are invalidated. We can use this freedom to
// erase by moving towards the end.
if (b == this->begin()) {
this->ptr = e;
} else {
const T *const end = this->end();
// move (by assignment) the elements from e to end
@ -815,41 +814,9 @@ public:
++b;
++e;
}
// destroy the final elements at the end
// here, b points to the new end and e to the actual end
do {
// Exceptions or not, dtor called once per instance
--this->size;
(--e)->~T();
} while (e != b);
}
void erase(GrowsBackwardsTag, T *b, T *e)
{
Q_ASSERT(this->isMutable());
Q_ASSERT(b < e);
Q_ASSERT(b >= this->begin() && b < this->end());
Q_ASSERT(e > this->begin() && e <= this->end());
const T *const begin = this->begin();
// move (by assignment) the elements from begin to b
// onto the new begin to e
while (b != begin) {
--b;
--e;
*e = std::move(*b);
}
// destroy the final elements at the begin
// here, e points to the new begin and b to the actual begin
do {
// Exceptions or not, dtor called once per instance
++this->ptr;
--this->size;
(b++)->~T();
} while (b != e);
this->size -= (e - b);
std::destroy(b, e);
}
void eraseFirst()
@ -1040,41 +1007,25 @@ public:
// use moving emplace
using QGenericArrayOps<T>::emplace;
void erase(GrowsForwardTag, T *b, T *e)
void erase(T *b, T *e)
{
Q_ASSERT(this->isMutable());
Q_ASSERT(b < e);
Q_ASSERT(b >= this->begin() && b < this->end());
Q_ASSERT(e > this->begin() && e <= this->end());
typedef typename QArrayExceptionSafetyPrimitives<T>::Mover Mover;
// Comply with std::vector::erase(): erased elements and all after them
// are invalidated. However, erasing from the beginning effectively
// means that all iterators are invalidated. We can use this freedom to
// erase by moving towards the end.
Mover mover(e, static_cast<const T *>(this->end()) - e, this->size);
// destroy the elements we're erasing
do {
// Exceptions or not, dtor called once per instance
(--e)->~T();
} while (e != b);
std::destroy(b, e);
if (b == this->begin()) {
this->ptr = e;
} else if (e != this->end()) {
memmove(static_cast<void *>(b), static_cast<const void *>(e), (static_cast<const T *>(this->end()) - e)*sizeof(T));
}
void erase(GrowsBackwardsTag, T *b, T *e)
{
Q_ASSERT(this->isMutable());
Q_ASSERT(b < e);
Q_ASSERT(b >= this->begin() && b < this->end());
Q_ASSERT(e > this->begin() && e <= this->end());
typedef typename QArrayExceptionSafetyPrimitives<T>::Mover Mover;
Mover mover(this->ptr, b - static_cast<const T *>(this->begin()), this->size);
// destroy the elements we're erasing
do {
// Exceptions or not, dtor called once per instance
++this->ptr;
(b++)->~T();
} while (b != e);
this->size -= (e - b);
}
void reallocate(qsizetype alloc, QArrayData::AllocationOption option)
@ -1310,24 +1261,6 @@ public:
++this->size;
}
void erase(T *b, T *e)
{
Q_ASSERT(this->isMutable());
Q_ASSERT(b < e);
Q_ASSERT(b >= this->begin() && b < this->end());
Q_ASSERT(e > this->begin() && e <= this->end());
// Comply with std::vector::erase(): erased elements and all after them
// are invalidated. However, erasing from the beginning effectively
// means that all iterators are invalidated. We can use this freedom to
// erase by moving towards the end.
if (b == this->begin()) {
Base::erase(GrowsBackwardsTag{}, b, e);
} else {
Base::erase(GrowsForwardTag{}, b, e);
}
}
void eraseFirst()
{
Q_ASSERT(this->isMutable());