QArrayDataOps: fix appendUninitialized()

... to be strongly exception-safe and call placement new correctly.

The old code could call user-provided overloads of new, because it
didn't cast the pointer to void* before calling placement-new. It
would also "randomly" stop when an exception was thrown. It correctly
updated the size of the container only after successful construction
of the element, but that's just weak exception-safety. There's no
reason this operation shouldn't be strongly exception-safe.

So instead of a raw loop around raw placement-new, use the
corresponding raw memory STL algorithms. They handle exceptions and
call placement-new correctly, and the code is both simpler as a
consequence, and more efficient, as it updates this->size only once.

Found in API-review.

Amends 73bf1c1a9bcc2615370d6a199420da0c6f380a44.

Change-Id: I535f393a3c378e1eea104bd3a05a274b9ec17964
Reviewed-by: Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
(cherry picked from commit f699248c0f3ad44f09a422304870f40c1998a1a5)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
Marc Mutz 2024-07-02 11:23:09 +02:00 committed by Qt Cherry-pick Bot
parent ea1978c7f9
commit e6fc32a9c5

View File

@ -969,15 +969,14 @@ public:
Q_ASSERT(newSize > this->size);
Q_ASSERT(newSize - this->size <= this->freeSpaceAtEnd());
T *const b = this->begin();
do {
auto ptr = b + this->size;
if constexpr (std::is_constructible_v<T, Qt::Initialization>)
new (ptr) T(Qt::Uninitialized);
else
new (ptr) T; // not T() -- default-construct
} while (++this->size != newSize);
T *const b = this->begin() + this->size;
T *const e = this->begin() + newSize;
if constexpr (std::is_constructible_v<T, Qt::Initialization>)
std::uninitialized_fill(b, e, Qt::Uninitialized);
else
std::uninitialized_default_construct(b, e);
this->size = newSize;
}
};