Add rvalue overload of insert/prepend to QVarLengthArray and QVector
Improves performance and STL compatibility by adding rvalue versions of prepend and insert. [ChangeLog][QtCore][QVarLengthArray] Added rvalue overloads of prepend and insert. [ChangeLog][QtCore][QVector] Added rvalue overloads of prepend and insert. [ChangeLog][QtCore][QVarLengthArray] Can now contain movable but non-copyable types, such as std::unique_ptr. Change-Id: I6c946acc5b67502c91c52ac5dea67cedb1af93a5 Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>bb10
parent
0d23b7f074
commit
dd58ddd5d9
|
|
@ -178,7 +178,9 @@ public:
|
|||
inline QVarLengthArray<T, Prealloc> &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<T, Prealloc>::realloc(int asize, int a
|
|||
s = 0;
|
||||
if (!QTypeInfoQuery<T>::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<T, Prealloc>::value(int i, const T &defau
|
|||
return (uint(i) >= uint(size())) ? defaultValue : at(i);
|
||||
}
|
||||
|
||||
template <class T, int Prealloc>
|
||||
inline void QVarLengthArray<T, Prealloc>::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 <class T, int Prealloc>
|
||||
inline void QVarLengthArray<T, Prealloc>::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<T, Prealloc>::remove(int i)
|
|||
{ Q_ASSERT_X(i >= 0 && i < s, "QVarLengthArray::remove", "index out of range");
|
||||
erase(begin() + i, begin() + i + 1); }
|
||||
template <class T, int Prealloc>
|
||||
inline void QVarLengthArray<T, Prealloc>::prepend(T &&t)
|
||||
{ insert(cbegin(), std::move(t)); }
|
||||
template <class T, int Prealloc>
|
||||
inline void QVarLengthArray<T, Prealloc>::prepend(const T &t)
|
||||
{ insert(begin(), 1, t); }
|
||||
|
||||
|
|
@ -454,6 +464,34 @@ inline void QVarLengthArray<T, Prealloc>::replace(int i, const T &t)
|
|||
data()[i] = copy;
|
||||
}
|
||||
|
||||
template <class T, int Prealloc>
|
||||
Q_OUTOFLINE_TEMPLATE typename QVarLengthArray<T, Prealloc>::iterator QVarLengthArray<T, Prealloc>::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<T>::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 <class T, int Prealloc>
|
||||
Q_OUTOFLINE_TEMPLATE typename QVarLengthArray<T, Prealloc>::iterator QVarLengthArray<T, Prealloc>::insert(const_iterator before, size_type n, const T &t)
|
||||
|
|
|
|||
|
|
@ -137,7 +137,9 @@ public:
|
|||
void append(T &&t);
|
||||
#endif
|
||||
inline void append(const QVector<T> &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<T>::insert(int i, int n, const T &t)
|
|||
{ Q_ASSERT_X(i >= 0 && i <= d->size, "QVector<T>::insert", "index out of range");
|
||||
insert(begin() + i, n, t); }
|
||||
template <typename T>
|
||||
inline void QVector<T>::insert(int i, T &&t)
|
||||
{ Q_ASSERT_X(i >= 0 && i <= d->size, "QVector<T>::insert", "index out of range");
|
||||
insert(begin() + i, std::move(t)); }
|
||||
template <typename T>
|
||||
inline void QVector<T>::remove(int i, int n)
|
||||
{ Q_ASSERT_X(i >= 0 && n >= 0 && i + n <= d->size, "QVector<T>::remove", "index out of range");
|
||||
erase(d->begin() + i, d->begin() + i + n); }
|
||||
|
|
@ -446,6 +454,9 @@ inline void QVector<T>::remove(int i)
|
|||
template <typename T>
|
||||
inline void QVector<T>::prepend(const T &t)
|
||||
{ insert(begin(), 1, t); }
|
||||
template <typename T>
|
||||
inline void QVector<T>::prepend(T &&t)
|
||||
{ insert(begin(), std::move(t)); }
|
||||
|
||||
template <typename T>
|
||||
inline void QVector<T>::replace(int i, const T &t)
|
||||
|
|
@ -558,9 +569,19 @@ void QVector<T>::reallocData(const int asize, const int aalloc, QArrayData::Allo
|
|||
T *dst = x->begin();
|
||||
|
||||
if (!QTypeInfoQuery<T>::isRelocatable || (isShared && QTypeInfo<T>::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<T>::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<void *>(dst), static_cast<void *>(srcBegin), (srcEnd - srcBegin) * sizeof(T));
|
||||
|
|
@ -573,12 +594,17 @@ void QVector<T>::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<T>::isComplex) {
|
||||
::memset(static_cast<void *>(dst), 0, (static_cast<T *>(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<T>::iterator QVector<T>::insert(iterator before, size_type n, c
|
|||
return d->begin() + offset;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
typename QVector<T>::iterator QVector<T>::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<T>::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 T>
|
||||
typename QVector<T>::iterator QVector<T>::erase(iterator abegin, iterator aend)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -53,6 +53,9 @@ private slots:
|
|||
void initializeListInt();
|
||||
void initializeListMovable();
|
||||
void initializeListComplex();
|
||||
void insertMove();
|
||||
void nonCopyable();
|
||||
|
||||
private:
|
||||
template<typename T>
|
||||
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<MyMovable, 4> 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<std::unique_ptr<int>> vec;
|
||||
std::unique_ptr<int> val1(new int(1));
|
||||
std::unique_ptr<int> val2(new int(2));
|
||||
std::unique_ptr<int> val3(new int(3));
|
||||
std::unique_ptr<int> 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"
|
||||
|
|
|
|||
|
|
@ -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<typename T> void copyConstructor() const;
|
||||
template<typename T> void add() const;
|
||||
|
|
@ -2861,6 +2890,34 @@ void tst_QVector::detachThreadSafetyCustom() const
|
|||
}
|
||||
}
|
||||
|
||||
void tst_QVector::insertMove() const
|
||||
{
|
||||
const int instancesCount = Movable::counter.loadAcquire();
|
||||
{
|
||||
QVector<Movable> 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"
|
||||
|
|
|
|||
Loading…
Reference in New Issue