QList: add uninitialized resizes
Creating a QList of a given size, or resizing it to a given size, will always value-initialize its elements. This commit adds support for uninitialized construction and resizes. The intended use case is using a QList as storage-to-be-overwritten: QList<int> list(size, Qt::Uninitialized); fillWithData(list.data(), list.size); How do we define "uninitialized": 1) if T is constructible using Qt::Uninitialized, use that; 2) otherwise, default-construct T. In detail: 1) covers (Qt-ish) datatypes that have a default constructor that initializes them, but also a dedicated constructor that doesn't initialize (e.g. QPoint, QQuaternion, ...). 2) covers everything else. Default initialization of scalars and trivially constructible datatypes will leave them uninitialized. A type which isn't trivially constructible will still get its default constructor called (and possibly actually gets initialized); we can't really do better than that, as we still have to construct objects and start their lifetimes. [ChangeLog][QtCore][QList] Added support for uninitialized construction and resizing. Change-Id: I32c285c7dddbf7e01475943f24e14e824bb13090 Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io> Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
parent
277d77029d
commit
73bf1c1a9b
@ -7,6 +7,7 @@
|
||||
|
||||
#include <QtCore/qarraydata.h>
|
||||
#include <QtCore/qcontainertools_impl.h>
|
||||
#include <QtCore/qnamespace.h>
|
||||
|
||||
#include <memory>
|
||||
#include <new>
|
||||
@ -960,6 +961,24 @@ public:
|
||||
// b might be updated so use [b, n)
|
||||
this->copyAppend(b, b + n);
|
||||
}
|
||||
|
||||
void appendUninitialized(qsizetype newSize)
|
||||
{
|
||||
Q_ASSERT(this->isMutable());
|
||||
Q_ASSERT(!this->isShared());
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace QtPrivate
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include <QtCore/qhashfunctions.h>
|
||||
#include <QtCore/qiterator.h>
|
||||
#include <QtCore/qcontainertools_impl.h>
|
||||
#include <QtCore/qnamespace.h>
|
||||
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
@ -324,6 +325,13 @@ public:
|
||||
inline explicit QList(const String &str)
|
||||
{ append(str); }
|
||||
|
||||
QList(qsizetype size, Qt::Initialization)
|
||||
: d(size)
|
||||
{
|
||||
if (size)
|
||||
d->appendUninitialized(size);
|
||||
}
|
||||
|
||||
// compiler-generated special member functions are fine!
|
||||
|
||||
void swap(QList &other) noexcept { d.swap(other.d); }
|
||||
@ -404,6 +412,12 @@ public:
|
||||
if (size > this->size())
|
||||
d->copyAppend(size - this->size(), c);
|
||||
}
|
||||
void resizeForOverwrite(qsizetype size)
|
||||
{
|
||||
resize_internal(size);
|
||||
if (size > this->size())
|
||||
d->appendUninitialized(size);
|
||||
}
|
||||
|
||||
inline qsizetype capacity() const { return qsizetype(d->constAllocatedCapacity()); }
|
||||
void reserve(qsizetype size);
|
||||
|
@ -247,6 +247,31 @@
|
||||
\sa resize()
|
||||
*/
|
||||
|
||||
/*! \fn template <typename T> QList<T>::QList(qsizetype size, Qt::Initialization)
|
||||
\since 6.8
|
||||
|
||||
Constructs a list with an initial size of \a size elements.
|
||||
|
||||
QList will make an attempt at \b{not initializing} the elements.
|
||||
|
||||
//! [qlist-uninitialized-strategy]
|
||||
Specifically:
|
||||
|
||||
\list
|
||||
|
||||
\li if \c{T} has a constructor that accepts \c{Qt::Uninitialized},
|
||||
that constructor will be used to initialize the elements;
|
||||
|
||||
\li otherwise, each element is default constructed. For
|
||||
trivially constructible types (such as \c{int}, \c{float}, etc.)
|
||||
this is equivalent to not initializing them.
|
||||
|
||||
\endlist
|
||||
//! [qlist-uninitialized-strategy]
|
||||
|
||||
\sa resizeForOverwrite()
|
||||
*/
|
||||
|
||||
/*! \fn template <typename T> QList<T>::QList(qsizetype size, parameter_type value)
|
||||
|
||||
Constructs a list with an initial size of \a size elements.
|
||||
@ -444,6 +469,17 @@
|
||||
\sa size()
|
||||
*/
|
||||
|
||||
/*! \fn template <typename T> void QList<T>::resizeForOverwrite(qsizetype size)
|
||||
\since 6.8
|
||||
|
||||
Sets the size of the list to \a size. If \a size is less than the
|
||||
current size, elements are removed from the end. If \a size is
|
||||
greater than the current size, elements are added to the end; QList
|
||||
will make an attempt at \b{not initializing} these new elements.
|
||||
|
||||
\include qlist.qdoc qlist-uninitialized-strategy
|
||||
*/
|
||||
|
||||
/*! \fn template <typename T> qsizetype QList<T>::capacity() const
|
||||
|
||||
Returns the maximum number of items that can be stored in the
|
||||
|
@ -322,6 +322,7 @@ private slots:
|
||||
void resizeToZero() const;
|
||||
void resizeToTheSameSize_data();
|
||||
void resizeToTheSameSize() const;
|
||||
void resizeForOverwrite() const;
|
||||
void iterators() const;
|
||||
void constIterators() const;
|
||||
void reverseIterators() const;
|
||||
@ -2531,6 +2532,51 @@ void tst_QList::resizeToTheSameSize() const
|
||||
QCOMPARE(y.size(), x.size());
|
||||
}
|
||||
|
||||
void tst_QList::resizeForOverwrite() const
|
||||
{
|
||||
constexpr int BUILD_COUNT = 42;
|
||||
{
|
||||
// Smoke test
|
||||
QList<int> l(BUILD_COUNT, Qt::Uninitialized);
|
||||
l.resizeForOverwrite(l.size() + BUILD_COUNT);
|
||||
}
|
||||
|
||||
{
|
||||
const int beforeCounter = Movable::counter.loadRelaxed();
|
||||
QList<Movable> l(BUILD_COUNT, Qt::Uninitialized);
|
||||
const int after1Counter = Movable::counter.loadRelaxed();
|
||||
QCOMPARE(after1Counter, beforeCounter + BUILD_COUNT);
|
||||
|
||||
l.resizeForOverwrite(l.size() + BUILD_COUNT);
|
||||
const int after2Counter = Movable::counter.loadRelaxed();
|
||||
QCOMPARE(after2Counter, after1Counter + BUILD_COUNT);
|
||||
}
|
||||
|
||||
struct QtInitializationSupport {
|
||||
bool wasInitialized;
|
||||
QtInitializationSupport() : wasInitialized(true) {}
|
||||
explicit QtInitializationSupport(Qt::Initialization) : wasInitialized(false) {}
|
||||
};
|
||||
|
||||
{
|
||||
QList<QtInitializationSupport> l(BUILD_COUNT);
|
||||
for (const auto &elem : l)
|
||||
QVERIFY(elem.wasInitialized);
|
||||
l.resize(l.size() + BUILD_COUNT);
|
||||
for (const auto &elem : l)
|
||||
QVERIFY(elem.wasInitialized);
|
||||
}
|
||||
|
||||
{
|
||||
QList<QtInitializationSupport> l(BUILD_COUNT, Qt::Uninitialized);
|
||||
for (const auto &elem : l)
|
||||
QVERIFY(!elem.wasInitialized);
|
||||
l.resizeForOverwrite(l.size() + BUILD_COUNT);
|
||||
for (const auto &elem : l)
|
||||
QVERIFY(!elem.wasInitialized);
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QList::iterators() const
|
||||
{
|
||||
QList<int> v;
|
||||
|
Loading…
x
Reference in New Issue
Block a user