diff --git a/src/corelib/tools/qvarlengtharray.h b/src/corelib/tools/qvarlengtharray.h index 27083442c5..f573855598 100644 --- a/src/corelib/tools/qvarlengtharray.h +++ b/src/corelib/tools/qvarlengtharray.h @@ -231,6 +231,10 @@ protected: } } + void assign_impl(qsizetype prealloc, void *array, qsizetype n, const T &t); + template + void assign_impl(qsizetype prealloc, void *array, Iterator first, Iterator last); + bool isValidIterator(const const_iterator &i) const { const std::less less = {}; @@ -258,6 +262,8 @@ class QVarLengthArray template using if_copyable = std::enable_if_t, bool>; + template + using if_input_iterator = QtPrivate::IfIsInputIterator; public: using size_type = typename Base::size_type; using value_type = typename Base::value_type; @@ -319,7 +325,7 @@ public: { } - template = true> + template = true> inline QVarLengthArray(InputIterator first, InputIterator last) : QVarLengthArray() { @@ -480,6 +486,15 @@ public: void insert(qsizetype i, T &&t); void insert(qsizetype i, const T &t); void insert(qsizetype i, qsizetype n, const T &t); + + void assign(qsizetype n, const T &t) + { Base::assign_impl(Prealloc, this->array, n, t); } + template = true> + void assign(InputIterator first, InputIterator last) + { Base::assign_impl(Prealloc, this->array, first, last); } + void assign(std::initializer_list list) + { assign(list.begin(), list.end()); } + #ifdef Q_QDOC void replace(qsizetype i, const T &t); void remove(qsizetype i, qsizetype n = 1); @@ -731,6 +746,51 @@ Q_OUTOFLINE_TEMPLATE void QVLABase::append_impl(qsizetype prealloc, void *arr this->s = asize; } +template +Q_OUTOFLINE_TEMPLATE void QVLABase::assign_impl(qsizetype prealloc, void *array, qsizetype n, const T &t) +{ + Q_ASSERT(n >= 0); + if (n > capacity()) { + reallocate_impl(prealloc, array, 0, capacity()); // clear + resize_impl(prealloc, array, n, t); + } else { + auto mid = (std::min)(n, size()); + std::fill(data(), data() + mid, t); + std::uninitialized_fill(data() + mid, data() + n, t); + s = n; + erase(data() + n, data() + size()); + } +} + +template +template +Q_OUTOFLINE_TEMPLATE void QVLABase::assign_impl(qsizetype prealloc, void *array, Iterator first, Iterator last) +{ + // This function only provides the basic exception guarantee. + if constexpr (std::is_convertible_v::iterator_category, std::forward_iterator_tag>) { + const qsizetype n = std::distance(first, last); + if (n > capacity()) { + reallocate_impl(prealloc, array, 0, capacity()); // clear + reallocate_impl(prealloc, array, 0, n); // reserve n + } + } + + auto dst = begin(); + while (first != last && dst != end()) { + *dst = *first; // reuse initialized buffer + ++dst; + ++first; + } + + qsizetype n = dst - begin(); + while (first != last) { + emplace_back_impl(prealloc, array, *first); + ++first; + ++n; + } + erase(data() + n, data() + size()); +} + template Q_OUTOFLINE_TEMPLATE void QVLABase::reallocate_impl(qsizetype prealloc, void *array, qsizetype asize, qsizetype aalloc) { diff --git a/src/corelib/tools/qvarlengtharray.qdoc b/src/corelib/tools/qvarlengtharray.qdoc index 4f41304c03..3ad8a82857 100644 --- a/src/corelib/tools/qvarlengtharray.qdoc +++ b/src/corelib/tools/qvarlengtharray.qdoc @@ -103,11 +103,14 @@ lists. */ -/*! \fn template template QVarLengthArray::QVarLengthArray(InputIterator first, InputIterator last) +/*! \fn template template> QVarLengthArray::QVarLengthArray(InputIterator first, InputIterator last) \since 5.14 Constructs an array with the contents in the iterator range [\a first, \a last). + This constructor only participates in overload resolution if + \c InputIterator models the \c std::input_iterator concept. + The value type of \c InputIterator must be convertible to \c T. */ @@ -992,3 +995,38 @@ \sa erase() */ + +/*! \fn template void QVarLengthArray::assign(qsizetype n, const T &t) + \since 6.6 + + Replaces the contents of this container with \a n copies of \a t. + + The size of this container will be equal to \a n. This function will only + allocate memory if \a n exceeds the capacity of the container. +*/ + +/*! \fn template template > void QVarLengthArray::assign(InputIterator first, InputIterator last) + \since 6.6 + + Replaces the contents of this container with a copy of the elements in the + iterator range [\a first, \a last). + + The size of this container will be equal to the number of elements in the + range [\a first, \a last). This function will only allocate memory if the + number of elements in the range exceeds the capacity of the container. + + This function overload only participates in overload resolution if + \c InputIterator models the \c std::input_iterator concept. + The behavior is undefined if either argument is an iterator into *this. +*/ + +/*! \fn template void QVarLengthArray::assign(std::initializer_list list) + \since 6.6 + + Replaces the contents of this container with a copy of the elements of \a list. + + The size of this container will be equal to the number of elements in \a list. + + This function only allocates memory if the number of elements in \a list + exceeds the capacity of the container. +*/ diff --git a/tests/auto/corelib/tools/containerapisymmetry/tst_containerapisymmetry.cpp b/tests/auto/corelib/tools/containerapisymmetry/tst_containerapisymmetry.cpp index c3a56b68ec..92d4863aab 100644 --- a/tests/auto/corelib/tools/containerapisymmetry/tst_containerapisymmetry.cpp +++ b/tests/auto/corelib/tools/containerapisymmetry/tst_containerapisymmetry.cpp @@ -331,6 +331,16 @@ private Q_SLOTS: void resize_QString() { resize_impl(); } void resize_QByteArray() { resize_impl(); } +private: + template + void assign_impl() const; + +private Q_SLOTS: + void assign_std_vector() + { assign_impl>(); }; + void assign_QVarLengthArray() + { assign_impl>(); }; + private: template void front_back_impl() const; @@ -760,6 +770,71 @@ void tst_ContainerApiSymmetry::resize_impl() const } template +void tst_ContainerApiSymmetry::assign_impl() const +{ +#define CHECK(Arr, ComparisonData, Sz_n, Sz_e, Cap_n, Cap_e) \ + QCOMPARE(Sz_n, Sz_e); \ + QCOMPARE(Cap_n, Cap_e); \ + for (const auto &e : Arr) \ + QCOMPARE(e, ComparisonData) \ + /*end*/ + using V = typename Container::value_type; + using S = typename Container::size_type; + auto tData = V(9); + { + // fill version + auto c = make(4); + c.assign(4, tData); + CHECK(c, tData, c.size(), S(4), c.capacity(), S(4)); + + c.assign(8, tData); + CHECK(c, tData, c.size(), S(8), c.capacity(), S(8)); + + c.assign(0, tData); + CHECK(c, tData, c.size(), S(0), c.capacity(), S(8)); + } + { + // range version for non input iterator + auto c = make(4); + auto iter = make(1); + + iter.assign(8, tData); + c.assign(iter.begin(), iter.end()); + CHECK(c, tData, c.size(), S(8), c.capacity(), S(8)); + } + { + // range version for input iterator + auto c = make(4); + + std::stringstream ss("9 9 "); + c.assign(std::istream_iterator{ss}, std::istream_iterator{}); + CHECK(c, tData, c.size(), S(2), c.capacity(), S(4)); + + ss.str(""); + ss.clear(); + ss << "9 9 9 9 "; + c.assign(std::istream_iterator{ss}, std::istream_iterator{}); + CHECK(c, tData, c.size(), S(4), c.capacity(), S(4)); + + ss.str(""); + ss.clear(); + ss << "9 9 9 9 9 9 9 "; + c.assign(std::istream_iterator{ss}, std::istream_iterator{}); + // We cannot check the capacity here because growth rates differ between implementations. + CHECK(c, tData, c.size(), S(7), 8, S(8)); + } + { + // initializer-list version + auto c = make(4); + std::initializer_list list = {tData, tData, tData}; + c.assign(list); + CHECK(c, tData, c.size(), S(3), c.capacity(), S(4)); + } + +#undef CHECK +} + +template void tst_ContainerApiSymmetry::front_back_impl() const { using V = typename Container::value_type;