diff options
author | Vitaly Fanaskov <vitaly.fanaskov@qt.io> | 2019-12-12 11:28:52 +0100 |
---|---|---|
committer | Vitaly Fanaskov <vitaly.fanaskov@qt.io> | 2020-02-10 18:28:43 +0100 |
commit | f19fbbdb2f5a3dbf3bac7cdbb4f7e8efe34f1e0d (patch) | |
tree | 75e0ccaf9b7c4d4444012c6b1ed7523ae91633c1 /src | |
parent | 71436d5499a9e0355f778f47ef465486a727049d (diff) |
QVector: implement methods for adding new elements constructed in place
Fixes: QTBUG-80293
Change-Id: I687dc05a9ad2bad7bab3dc2b1173edf75550d57e
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
Diffstat (limited to 'src')
-rw-r--r-- | src/corelib/doc/snippets/code/src_corelib_tools_qvector.cpp | 23 | ||||
-rw-r--r-- | src/corelib/tools/qarraydataops.h | 58 | ||||
-rw-r--r-- | src/corelib/tools/qvector.h | 64 | ||||
-rw-r--r-- | src/corelib/tools/qvector.qdoc | 53 |
4 files changed, 147 insertions, 51 deletions
diff --git a/src/corelib/doc/snippets/code/src_corelib_tools_qvector.cpp b/src/corelib/doc/snippets/code/src_corelib_tools_qvector.cpp index a05233049f..76a8d68f64 100644 --- a/src/corelib/doc/snippets/code/src_corelib_tools_qvector.cpp +++ b/src/corelib/doc/snippets/code/src_corelib_tools_qvector.cpp @@ -115,6 +115,29 @@ vector.append(std::move(three)); //! [move-append] +//! [emplace] +QVector<QString> vector{"a", "ccc"}; +vector.emplace(1, 2, 'b'); +// vector: ["a", "bb", "ccc"] +//! [emplace] + + +//! [emplace-back] +QVector<QString> vector{"one", "two"}; +vector.emplaceBack(3, 'a'); +qDebug() << vector; +// vector: ["one", "two", "aaa"] +//! [emplace-back] + + +//! [emplace-back-ref] +QVector<QString> vector; +auto &ref = vector.emplaceBack(); +ref = "one"; +// vector: ["one"] +//! [emplace-back-ref] + + //! [8] QVector<QString> vector; vector.prepend("one"); diff --git a/src/corelib/tools/qarraydataops.h b/src/corelib/tools/qarraydataops.h index 0d2be5d51c..1d74f49993 100644 --- a/src/corelib/tools/qarraydataops.h +++ b/src/corelib/tools/qarraydataops.h @@ -117,6 +117,9 @@ struct QPodArrayOps this->size += int(n); } + template <typename ...Args> + void emplaceBack(Args&&... args) { this->emplace(this->end(), T(std::forward<Args>(args)...)); } + void truncate(size_t newSize) { Q_ASSERT(this->isMutable()); @@ -163,16 +166,28 @@ struct QPodArrayOps *where++ = t; } - void insert(T *where, T &&t) + template <typename ...Args> + void createInPlace(T *where, Args&&... args) { new (where) T(std::forward<Args>(args)...); } + + template <typename ...Args> + void emplace(T *where, Args&&... args) { Q_ASSERT(!this->isShared()); Q_ASSERT(where >= this->begin() && where <= this->end()); Q_ASSERT(this->allocatedCapacity() - this->size >= 1); - ::memmove(static_cast<void *>(where + 1), static_cast<void *>(where), - (static_cast<const T*>(this->end()) - where) * sizeof(T)); - this->size += 1; - new (where) T(std::move(t)); + if (where == this->end()) { + new (this->end()) T(std::forward<Args>(args)...); + } else { + // Preserve the value, because it might be a reference to some part of the moved chunk + T t(std::forward<Args>(args)...); + + ::memmove(static_cast<void *>(where + 1), static_cast<void *>(where), + (static_cast<const T*>(this->end()) - where) * sizeof(T)); + *where = t; + } + + ++this->size; } @@ -289,6 +304,12 @@ struct QGenericArrayOps } } + template <typename ...Args> + void emplaceBack(Args&&... args) + { + this->emplace(this->end(), std::forward<Args>(args)...); + } + void truncate(size_t newSize) { Q_ASSERT(this->isMutable()); @@ -444,31 +465,20 @@ struct QGenericArrayOps } } - void insert(T *where, T &&t) + template <typename ...Args> + void createInPlace(T *where, Args&&... args) { new (where) T(std::forward<Args>(args)...); } + + template <typename iterator, typename ...Args> + void emplace(iterator where, Args&&... args) { Q_ASSERT(!this->isShared()); Q_ASSERT(where >= this->begin() && where <= this->end()); Q_ASSERT(this->allocatedCapacity() - this->size >= 1); - // Array may be truncated at where in case of exceptions - T *const end = this->end(); - - if (where != end) { - // Move elements in array - T *readIter = end - 1; - T *writeIter = end; - new (writeIter) T(std::move(*readIter)); - while (readIter > where) { - --readIter; - --writeIter; - *writeIter = std::move(*readIter); - } - *where = std::move(t); - } else { - new (where) T(std::move(t)); - } - + createInPlace(this->end(), std::forward<Args>(args)...); ++this->size; + + std::rotate(where, this->end() - 1, this->end()); } void erase(T *b, T *e) diff --git a/src/corelib/tools/qvector.h b/src/corelib/tools/qvector.h index 3e98de41f4..2cfcb2e5dc 100644 --- a/src/corelib/tools/qvector.h +++ b/src/corelib/tools/qvector.h @@ -242,10 +242,14 @@ public: void append(const_reference t) { append(const_iterator(std::addressof(t)), const_iterator(std::addressof(t)) + 1); } void append(const_iterator i1, const_iterator i2); - void append(value_type &&t); + void append(rvalue_ref t) { emplaceBack(std::move(t)); } void append(const QVector<T> &l) { append(l.constBegin(), l.constEnd()); } void prepend(rvalue_ref t); void prepend(const T &t); + + template <typename ...Args> + reference emplaceBack(Args&&... args) { return *emplace(count(), std::forward<Args>(args)...); } + iterator insert(int i, parameter_type t) { return insert(i, 1, t); } iterator insert(int i, int n, parameter_type t); @@ -264,7 +268,17 @@ public: Q_ASSERT_X(isValidIterator(before), "QVector::insert", "The specified iterator argument 'before' is invalid"); return insert(std::distance(constBegin(), before), std::move(t)); } - iterator insert(int i, rvalue_ref t); + iterator insert(int i, rvalue_ref t) { return emplace(i, std::move(t)); } + + template <typename ...Args> + iterator emplace(const_iterator before, Args&&... args) + { + Q_ASSERT_X(isValidIterator(before), "QVector::emplace", "The specified iterator argument 'before' is invalid"); + return emplace(std::distance(constBegin(), before), std::forward<Args>(args)...); + } + + template <typename ...Args> + iterator emplace(int i, Args&&... args); #if 0 template< class InputIt > iterator insert( const_iterator pos, InputIt first, InputIt last ); @@ -388,6 +402,10 @@ public: inline void push_front(const T &t) { prepend(t); } void pop_back() { removeLast(); } void pop_front() { removeFirst(); } + + template <typename ...Args> + reference emplace_back(Args&&... args) { return emplaceBack(std::forward<Args>(args)...); } + inline bool empty() const { return d->size == 0; } inline reference front() { return first(); } @@ -538,27 +556,6 @@ inline void QVector<T>::append(const_iterator i1, const_iterator i2) } template <typename T> -inline void QVector<T>::append(value_type &&t) -{ - const size_t newSize = size() + 1; - const bool isTooSmall = newSize > d->allocatedCapacity(); - const bool isOverlapping = std::addressof(*d->begin()) <= std::addressof(t) - && std::addressof(t) < std::addressof(*d->end()); - if (isTooSmall || d->needsDetach() || Q_UNLIKELY(isOverlapping)) { - typename Data::ArrayOptions flags = d->detachFlags(); - if (isTooSmall) - flags |= Data::GrowsForward; - DataPointer detached(Data::allocate(d->detachCapacity(newSize), flags)); - detached->copyAppend(constBegin(), constEnd()); - detached->moveAppend(std::addressof(t), std::addressof(t) + 1); - d.swap(detached); - } else { - // we're detached and we can just move data around - d->moveAppend(std::addressof(t), std::addressof(t) + 1); - } -} - -template <typename T> inline typename QVector<T>::iterator QVector<T>::insert(int i, int n, parameter_type t) { @@ -592,10 +589,11 @@ QVector<T>::insert(int i, int n, parameter_type t) } template <typename T> +template <typename ...Args> typename QVector<T>::iterator -QVector<T>::insert(int i, rvalue_ref t) +QVector<T>::emplace(int i, Args&&... args) { - Q_ASSERT_X(size_t(i) <= size_t(d->size), "QVector<T>::insert", "index out of range"); + Q_ASSERT_X(i >= 0 && i <= d->size, "QVector<T>::insert", "index out of range"); const size_t newSize = size() + 1; if (d->needsDetach() || newSize > d->allocatedCapacity()) { @@ -605,12 +603,24 @@ QVector<T>::insert(int i, rvalue_ref t) DataPointer detached(Data::allocate(d->detachCapacity(newSize), flags)); const_iterator where = constBegin() + i; + + // First, create an element to handle cases, when a user moves + // the element from a container to the same container + detached->createInPlace(detached.begin() + i, std::forward<Args>(args)...); + + // Then, put the first part of the elements to the new location detached->copyAppend(constBegin(), where); - detached->moveAppend(std::addressof(t), std::addressof(t) + 1); + + // After that, increase the actual size, because we created + // one extra element + ++detached.size; + + // Finally, put the rest of the elements to the new location detached->copyAppend(where, constEnd()); + d.swap(detached); } else { - d->insert(d.begin() + i, std::move(t)); + d->emplace(d.begin() + i, std::forward<Args>(args)...); } return d.begin() + i; } diff --git a/src/corelib/tools/qvector.qdoc b/src/corelib/tools/qvector.qdoc index 116d962411..a5ea4073f8 100644 --- a/src/corelib/tools/qvector.qdoc +++ b/src/corelib/tools/qvector.qdoc @@ -646,6 +646,28 @@ \sa append(), insert() */ +/*! + \fn template <typename T> template <typename ...Args> T &QVector<T>::emplaceBack(Args&&... args) + \fn template <typename T> template <typename ...Args> T &QVector<T>::emplace_back(Args&&... args) + + Adds a new element to the end for the container. This new element + is constructed in-place using \a args as the arguments for its + construction. + + Returns a reference to the new element. + + Example: + \snippet code/src_corelib_tools_qvector.cpp emplace-back + + It is also possible to access a newly created object by using + returned reference: + \snippet code/src_corelib_tools_qvector.cpp emplace-back-ref + + This is the same as vector.emplace(vector.size(), \a args). + + \sa emplace +*/ + /*! \fn template <typename T> void QVector<T>::insert(int i, const T &value) \fn template <typename T> void QVector<T>::insert(int i, T &&value) @@ -693,6 +715,26 @@ first of the inserted items. */ +/*! + \fn template <typename T> template <typename ...Args> QVector<T>::iterator QVector<T>::emplace(int i, Args&&... args) + + Extends the container by inserting a new element at position \a i. + This new element is constructed in-place using \a args as the + arguments for its construction. + + Returns an iterator to the new element. + + Example: + \snippet code/src_corelib_tools_qvector.cpp emplace + + \note It is garanteed that the element will be created in place + at the beginning, but after that it might be copied or + moved to the right position. + + \sa emplaceBack +*/ + + /*! \fn template <typename T> void QVector<T>::replace(int i, const T &value) Replaces the item at index position \a i with \a value. @@ -838,6 +880,17 @@ \sa takeFirst(), removeLast() */ +/*! + \fn template <typename T> template <typename ...Args> QVector<T>::iterator QVector<T>::emplace(QVector<T>::iterator before, Args&&... args) + + \overload + + Creates a new element in front of the item pointed to by the + iterator \a before. This new element is constructed in-place + using \a args as the arguments for its construction. + + Returns an iterator to the new element. +*/ /*! \fn template <typename T> QVector<T> &QVector<T>::fill(const T &value, int size = -1) |