QVarLengthArray: add STL-style assign()
Implemented assign() methods for QVarLengthArray to align with the criteria of std::vector, addressing the previously missing functionality. Reference: https://en.cppreference.com/w/cpp/container/vector/assign [ChangeLog][QtCore][QVarLengthArray] Added assign(). Fixes: QTBUG-106200 Change-Id: If671069808ff561b0f4c77b6c7f7aca360a0c663 Reviewed-by: Marc Mutz <marc.mutz@qt.io>
This commit is contained in:
parent
65cda1f06a
commit
7cbdc8abbd
@ -231,6 +231,10 @@ protected:
|
||||
}
|
||||
}
|
||||
|
||||
void assign_impl(qsizetype prealloc, void *array, qsizetype n, const T &t);
|
||||
template <typename Iterator>
|
||||
void assign_impl(qsizetype prealloc, void *array, Iterator first, Iterator last);
|
||||
|
||||
bool isValidIterator(const const_iterator &i) const
|
||||
{
|
||||
const std::less<const T *> less = {};
|
||||
@ -258,6 +262,8 @@ class QVarLengthArray
|
||||
|
||||
template <typename U>
|
||||
using if_copyable = std::enable_if_t<std::is_copy_constructible_v<U>, bool>;
|
||||
template <typename InputIterator>
|
||||
using if_input_iterator = QtPrivate::IfIsInputIterator<InputIterator>;
|
||||
public:
|
||||
using size_type = typename Base::size_type;
|
||||
using value_type = typename Base::value_type;
|
||||
@ -319,7 +325,7 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
template <typename InputIterator, QtPrivate::IfIsInputIterator<InputIterator> = true>
|
||||
template <typename InputIterator, if_input_iterator<InputIterator> = 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 <typename InputIterator, if_input_iterator<InputIterator> = true>
|
||||
void assign(InputIterator first, InputIterator last)
|
||||
{ Base::assign_impl(Prealloc, this->array, first, last); }
|
||||
void assign(std::initializer_list<T> 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<T>::append_impl(qsizetype prealloc, void *arr
|
||||
this->s = asize;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
Q_OUTOFLINE_TEMPLATE void QVLABase<T>::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 <class T>
|
||||
template <typename Iterator>
|
||||
Q_OUTOFLINE_TEMPLATE void QVLABase<T>::assign_impl(qsizetype prealloc, void *array, Iterator first, Iterator last)
|
||||
{
|
||||
// This function only provides the basic exception guarantee.
|
||||
if constexpr (std::is_convertible_v<typename std::iterator_traits<Iterator>::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 <class T>
|
||||
Q_OUTOFLINE_TEMPLATE void QVLABase<T>::reallocate_impl(qsizetype prealloc, void *array, qsizetype asize, qsizetype aalloc)
|
||||
{
|
||||
|
@ -103,11 +103,14 @@
|
||||
lists.
|
||||
*/
|
||||
|
||||
/*! \fn template<class T, qsizetype Prealloc> template<typename InputIterator> QVarLengthArray<T, Prealloc>::QVarLengthArray(InputIterator first, InputIterator last)
|
||||
/*! \fn template<class T, qsizetype Prealloc> template<typename InputIterator, if_input_iterator<InputIterator>> QVarLengthArray<T, Prealloc>::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 <class T, qsizetype Prealloc> void QVarLengthArray<T, Prealloc>::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 <class T, qsizetype Prealloc> template <typename InputIterator, if_input_iterator<InputIterator>> void QVarLengthArray<T, Prealloc>::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 <class T, qsizetype Prealloc> void QVarLengthArray<T, Prealloc>::assign(std::initializer_list<T> 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.
|
||||
*/
|
||||
|
@ -331,6 +331,16 @@ private Q_SLOTS:
|
||||
void resize_QString() { resize_impl<QString>(); }
|
||||
void resize_QByteArray() { resize_impl<QByteArray>(); }
|
||||
|
||||
private:
|
||||
template<typename Container>
|
||||
void assign_impl() const;
|
||||
|
||||
private Q_SLOTS:
|
||||
void assign_std_vector()
|
||||
{ assign_impl<std::vector<int>>(); };
|
||||
void assign_QVarLengthArray()
|
||||
{ assign_impl<QVarLengthArray<int, 4>>(); };
|
||||
|
||||
private:
|
||||
template <typename Container>
|
||||
void front_back_impl() const;
|
||||
@ -760,6 +770,71 @@ void tst_ContainerApiSymmetry::resize_impl() const
|
||||
}
|
||||
|
||||
template <typename Container>
|
||||
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<Container>(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<Container>(4);
|
||||
auto iter = make<Container>(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<Container>(4);
|
||||
|
||||
std::stringstream ss("9 9 ");
|
||||
c.assign(std::istream_iterator<V>{ss}, std::istream_iterator<V>{});
|
||||
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<V>{ss}, std::istream_iterator<V>{});
|
||||
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<V>{ss}, std::istream_iterator<V>{});
|
||||
// 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<Container>(4);
|
||||
std::initializer_list<V> list = {tData, tData, tData};
|
||||
c.assign(list);
|
||||
CHECK(c, tData, c.size(), S(3), c.capacity(), S(4));
|
||||
}
|
||||
|
||||
#undef CHECK
|
||||
}
|
||||
|
||||
template<typename Container>
|
||||
void tst_ContainerApiSymmetry::front_back_impl() const
|
||||
{
|
||||
using V = typename Container::value_type;
|
||||
|
Loading…
Reference in New Issue
Block a user