#if __glibcxx_ranges_to_container // C++ >= 23
/**
* @brief Construct a vector from a range.
+ * @param __rg A range of values that are convertible to `value_type`.
* @since C++23
*/
template<__detail::__container_compatible_range<bool> _Rg>
#if __glibcxx_ranges_to_container // C++ >= 23
/**
* @brief Assign a range to the vector.
+ * @param __rg A range of values that are convertible to `value_type`.
+ * @pre `__rg` and `*this` do not overlap.
* @since C++23
*/
template<__detail::__container_compatible_range<bool> _Rg>
assign_range(_Rg&& __rg)
{
static_assert(assignable_from<bool&, ranges::range_reference_t<_Rg>>);
- clear();
- append_range(std::forward<_Rg>(__rg));
+ if constexpr (ranges::forward_range<_Rg> || ranges::sized_range<_Rg>)
+ {
+ if (auto __n = size_type(ranges::distance(__rg)))
+ {
+ reserve(__n);
+ this->_M_impl._M_finish
+ = ranges::copy(std::forward<_Rg>(__rg), begin()).out;
+ }
+ else
+ clear();
+ }
+ else
+ {
+ clear();
+ auto __first = ranges::begin(__rg);
+ const auto __last = ranges::end(__rg);
+ for (; __first != __last; ++__first)
+ emplace_back(*__first);
+ }
}
#endif
#if __glibcxx_ranges_to_container // C++ >= 23
/**
* @brief Insert a range into the vector.
+ * @param __rg A range of values that are convertible to `bool`.
+ * @return An iterator that points to the first new element inserted,
+ * or to `__pos` if `__rg` is an empty range.
+ * @pre `__rg` and `*this` do not overlap.
* @since C++23
*/
template<__detail::__container_compatible_range<bool> _Rg>
constexpr void
append_range(_Rg&& __rg)
{
+ // N.B. __rg may overlap with *this, so we must copy from __rg before
+ // existing elements or iterators referring to *this are invalidated.
+ // e.g. in v.append_range(views::concat(v, foo)) rg overlaps v.
if constexpr (ranges::forward_range<_Rg> || ranges::sized_range<_Rg>)
{
- reserve(size() + size_type(ranges::distance(__rg)));
- this->_M_impl._M_finish = ranges::copy(__rg, end()).out;
+ const auto __n = size_type(ranges::distance(__rg));
+
+ // If there is no existing storage, there are no iterators that
+ // can be referring to our storage, so it's safe to allocate now.
+ if (capacity() == 0)
+ reserve(__n);
+
+ const auto __sz = size();
+ const auto __capacity = capacity();
+ if ((__capacity - __sz) >= __n)
+ {
+ this->_M_impl._M_finish
+ = ranges::copy(std::forward<_Rg>(__rg), end()).out;
+ return;
+ }
+
+ vector __tmp(get_allocator());
+ __tmp.reserve(_M_check_len(__n, "vector::append_range"));
+ __tmp._M_impl._M_finish
+ = _M_copy_aligned(cbegin(), cend(), __tmp.begin());
+ __tmp._M_impl._M_finish
+ = ranges::copy(std::forward<_Rg>(__rg), __tmp.end()).out;
+ swap(__tmp);
}
else
{
auto __first = ranges::begin(__rg);
const auto __last = ranges::end(__rg);
- size_type __n = size();
- const size_type __cap = capacity();
- for (; __first != __last && __n < __cap; ++__first, (void)++__n)
+
+ // Fill up to the end of current capacity.
+ for (auto __free = capacity() - size();
+ __first != __last && __free > 0;
+ ++__first, (void) --__free)
emplace_back(*__first);
- if (__first != __last)
- {
- ranges::subrange __rest(std::move(__first), __last);
- append_range(vector(from_range, __rest, get_allocator()));
- }
+
+ if (__first == __last)
+ return;
+
+ // Copy the rest of the range to a new vector.
+ ranges::subrange __rest(std::move(__first), __last);
+ vector __tmp(from_range, __rest, get_allocator());
+ insert(end(), __tmp.begin(), __tmp.end());
}
}
#endif // ranges_to_container
#if __cplusplus >= 202002L
# include <compare>
#endif
-#if __cplusplus > 202002L
+#if __glibcxx_ranges_to_container // C++ >= 23
# include <bits/ranges_algobase.h> // ranges::copy
# include <bits/ranges_util.h> // ranges::subrange
#endif
#if __glibcxx_ranges_to_container // C++ >= 23
/**
* @brief Construct a vector from a range.
+ * @param __rg A range of values that are convertible to `bool`.
* @since C++23
*/
template<__detail::__container_compatible_range<_Tp> _Rg>
_Base::_M_append_range(__rg);
}
else
- append_range(std::move(__rg));
+ {
+ auto __first = ranges::begin(__rg);
+ const auto __last = ranges::end(__rg);
+ for (; __first != __last; ++__first)
+ emplace_back(*__first);
+ }
}
#endif
#if __glibcxx_ranges_to_container // C++ >= 23
/**
* @brief Assign a range to the vector.
+ * @param __rg A range of values that are convertible to `value_type`.
+ * @pre `__rg` and `*this` do not overlap.
* @since C++23
*/
template<__detail::__container_compatible_range<_Tp> _Rg>
#if __glibcxx_ranges_to_container // C++ >= 23
/**
* @brief Insert a range into the vector.
+ * @param __rg A range of values that are convertible to `value_type`.
+ * @return An iterator that points to the first new element inserted,
+ * or to `__pos` if `__rg` is an empty range.
+ * @pre `__rg` and `*this` do not overlap.
* @since C++23
*/
template<__detail::__container_compatible_range<_Tp> _Rg>
/**
* @brief Append a range at the end of the vector.
+ * @param __rg A range of values that are convertible to `value_type`.
* @since C++23
*/
template<__detail::__container_compatible_range<_Tp> _Rg>
constexpr void
append_range(_Rg&& __rg)
{
+ // N.B. __rg may overlap with *this, so we must copy from __rg before
+ // existing elements or iterators referring to *this are invalidated.
+ // e.g. in v.append_range(views::concat(v, foo)) rg overlaps v.
if constexpr (ranges::forward_range<_Rg> || ranges::sized_range<_Rg>)
{
const auto __n = size_type(ranges::distance(__rg));
- reserve(size() + __n);
- _GLIBCXX_ASAN_ANNOTATE_GROW(__n);
- _Base::_M_append_range(__rg);
- _GLIBCXX_ASAN_ANNOTATE_GREW(__n);
+
+ // If there is no existing storage, there are no iterators that
+ // can be referring to our storage, so it's safe to allocate now.
+ if (capacity() == 0)
+ reserve(__n);
+
+ const auto __sz = size();
+ const auto __capacity = capacity();
+ if ((__capacity - __sz) >= __n)
+ {
+ _GLIBCXX_ASAN_ANNOTATE_GROW(__n);
+ _Base::_M_append_range(__rg);
+ _GLIBCXX_ASAN_ANNOTATE_GREW(__n);
+ return;
+ }
+
+ const size_type __len = _M_check_len(__n, "vector::append_range");
+
+ pointer __old_start = this->_M_impl._M_start;
+ pointer __old_finish = this->_M_impl._M_finish;
+
+ allocator_type& __a = _M_get_Tp_allocator();
+ const pointer __start = this->_M_allocate(__len);
+ const pointer __mid = __start + __sz;
+ const pointer __back = __mid + __n;
+ _Guard_alloc __guard(__start, __len, *this);
+ std::__uninitialized_copy_a(ranges::begin(__rg),
+ ranges::end(__rg),
+ __mid, __a);
+
+ if constexpr (_S_use_relocate())
+ _S_relocate(__old_start, __old_finish, __start, __a);
+ else
+ {
+ // RAII type to destroy initialized elements.
+ struct _Guard_elts
+ {
+ pointer _M_first, _M_last; // Elements to destroy
+ _Tp_alloc_type& _M_alloc;
+
+ constexpr
+ _Guard_elts(pointer __f, pointer __l, _Tp_alloc_type& __a)
+ : _M_first(__f), _M_last(__l), _M_alloc(__a)
+ { }
+
+ constexpr
+ ~_Guard_elts()
+ { std::_Destroy(_M_first, _M_last, _M_alloc); }
+
+ _Guard_elts(_Guard_elts&&) = delete;
+ };
+ _Guard_elts __guard_elts{__mid, __back, __a};
+
+ std::__uninitialized_move_a(__old_start, __old_finish,
+ __start, __a);
+
+ // Let old elements get destroyed by __guard_elts:
+ __guard_elts._M_first = __old_start;
+ __guard_elts._M_last = __old_finish;
+ }
+
+ // Now give ownership of old storage to __guard:
+ __guard._M_storage = __old_start;
+ __guard._M_len = __capacity;
+ // Finally, take ownership of new storage:
+ this->_M_impl._M_start = __start;
+ this->_M_impl._M_finish = __back;
+ this->_M_impl._M_end_of_storage = __start + __len;
}
else
{
auto __first = ranges::begin(__rg);
const auto __last = ranges::end(__rg);
- for (; __first != __last; ++__first)
+
+ // Fill up to the end of current capacity.
+ for (auto __free = capacity() - size();
+ __first != __last && __free > 0;
+ ++__first, (void) --__free)
emplace_back(*__first);
+
+ if (__first == __last)
+ return;
+
+ // Copy the rest of the range to a new vector.
+ vector __tmp(_M_get_Tp_allocator());
+ for (; __first != __last; ++__first)
+ __tmp.emplace_back(*__first);
+ reserve(_M_check_len(__tmp.size(), "vector::append_range"));
+ ranges::subrange __r(std::make_move_iterator(__tmp.begin()),
+ std::make_move_iterator(__tmp.end()));
+ append_range(__r); // This will take the fast path above.
}
}
#endif // ranges_to_container
return begin() + __ins_idx;
}
else
- return insert_range(__pos, vector(from_range, std::move(__rg),
+ return insert_range(__pos, vector(from_range, std::forward<_Rg>(__rg),
_M_get_Tp_allocator()));
}
#endif // ranges_to_container
{
// XXX: this doesn't test the non-forward_range code paths are constexpr.
do_test<std::span<short>, std::allocator<bool>>();
+
+ // Some basic tests for overlapping ranges in constant expressions.
+ using I = std::vector<bool>::iterator;
+
+ struct InputRange
+ {
+ struct Sent { I end; };
+
+ struct Iter
+ {
+ using value_type = bool;
+ using difference_type = int;
+ constexpr explicit Iter(I i) : i(i) { }
+ constexpr Iter& operator++() { ++i; return *this; }
+ constexpr Iter operator++(int) { auto i = *this; ++i; return i; }
+ constexpr int operator*() const { return *i; }
+ constexpr bool operator==(const Iter&) const = default;
+ constexpr bool operator==(const Sent& s) const { return i == s.end; }
+ I i;
+ };
+
+ Iter iter;
+ Sent sent;
+
+ constexpr InputRange(I f, I l) : iter{f}, sent{l} { }
+ constexpr Iter begin() const { return iter; }
+ constexpr Sent end() const { return sent; }
+ };
+ static_assert( std::ranges::input_range<InputRange> );
+ static_assert( ! std::ranges::forward_range<InputRange> );
+
+ std::vector<bool> vec(5);
+
+ // Test overlapping input ranges
+ vec.resize(vec.capacity());
+ vec.append_range(InputRange(vec.begin(), vec.begin() + 3)); // no capacity
+ vec.reserve(vec.capacity() + 2);
+ vec.append_range(InputRange(vec.begin(), vec.begin() + 4)); // some capacity
+ vec.reserve(vec.capacity() + 6);
+ vec.append_range(InputRange(vec.begin(), vec.begin() + 5)); // enough capacity
+
+ using R = std::ranges::subrange<I>;
+
+ // Test overlapping forward ranges
+ vec.resize(vec.capacity());
+ vec.append_range(R(vec.begin(), vec.begin() + 3)); // no capacity
+ vec.reserve(vec.size() + 2);
+ vec.append_range(R(vec.begin(), vec.begin() + 4)); // some capacity
+ vec.reserve(vec.size() + 6);
+ vec.append_range(R(vec.begin(), vec.begin() + 5)); // enough capacity
+
return true;
}
return true;
}
+void
+test_overlapping()
+{
+ using __gnu_test::test_input_range;
+ using __gnu_test::test_forward_range;
+
+ struct X {
+ unsigned* p;
+ constexpr X(int i = 0) : p(new unsigned(i)) { }
+ constexpr X(const X& m) : p(new unsigned(*m.p)) { }
+ constexpr X(X&& m) noexcept : p(m.p) { m.p = nullptr; }
+ constexpr ~X() { delete p; }
+ };
+
+ std::vector<X> vec;
+ unsigned size = 5;
+ vec.reserve(size);
+ for (unsigned i = 0; i < size; ++i)
+ vec.emplace_back(i);
+
+ // Append an input range that overlaps with vec.
+ {
+ __gnu_test::test_input_range<X> r(vec.data(), vec.data() + size);
+ vec.append_range(r);
+ VERIFY( vec.size() == 2 * size );
+ for (unsigned i = 0; i < size; ++i)
+ {
+ VERIFY( *vec[i].p == i );
+ VERIFY( *vec[i+size].p == i );
+ }
+ }
+
+ size = vec.size() - 2;
+ vec.resize(size);
+ for (unsigned i = 0; i < size; ++i)
+ *vec[i].p = i;
+
+ // Repeat with unused capacity in the vector.
+ {
+ __gnu_test::test_input_range<X> r(vec.data(), vec.data() + size);
+ vec.append_range(r);
+ VERIFY( vec.size() == 2 * size );
+ for (unsigned i = 0; i < size; ++i)
+ {
+ VERIFY( *vec[i].p == i );
+ VERIFY( *vec[i+size].p == i );
+ }
+ }
+
+ size = vec.size() - 2;
+ vec.resize(size);
+ for (unsigned i = 0; i < size; ++i)
+ *vec[i].p = i;
+
+ // Repeat with input range that doesn't overlap full vector.
+ {
+ __gnu_test::test_input_range<X> r(vec.data() + 1, vec.data() + 4);
+ vec.append_range(r);
+ VERIFY( vec.size() == size + 3 );
+ for (unsigned i = 0; i < size; ++i)
+ {
+ VERIFY( *vec[i].p == i );
+ if (i < 3)
+ VERIFY( *vec[i+size].p == i+1 );
+ }
+ }
+
+ size = 5;
+ vec.resize(size);
+ for (unsigned i = 0; i < size; ++i)
+ *vec[i].p = i;
+
+ // Append a forward range that overlaps with vec.
+ {
+ __gnu_test::test_forward_range<X> r(vec.data(), vec.data() + size);
+ vec.append_range(r);
+ VERIFY( vec.size() == 2 * size );
+ for (unsigned i = 0; i < size; ++i)
+ {
+ VERIFY( *vec[i].p == i );
+ VERIFY( *vec[i+size].p == i );
+ }
+ }
+
+ size = vec.size() - 2;
+ vec.resize(size);
+ for (unsigned i = 0; i < size; ++i)
+ *vec[i].p = i;
+
+ // Repeat with insufficient unused capacity in the vector.
+ {
+ __gnu_test::test_forward_range<X> r(vec.data(), vec.data() + size);
+ vec.append_range(r);
+ VERIFY( vec.size() == 2 * size );
+ for (unsigned i = 0; i < size; ++i)
+ {
+ VERIFY( *vec[i].p == i );
+ VERIFY( *vec[i+size].p == i );
+ }
+ }
+
+ size = vec.size() / 2;
+ vec.resize(size);
+
+ // Repeat with sufficient unused capacity in the vector.
+ {
+ __gnu_test::test_forward_range<X> r(vec.data(), vec.data() + size);
+ vec.append_range(r);
+ VERIFY( vec.size() == 2 * size );
+ for (unsigned i = 0; i < size; ++i)
+ {
+ VERIFY( *vec[i].p == i );
+ VERIFY( *vec[i+size].p == i );
+ }
+ }
+}
+
constexpr bool
test_constexpr()
{
// XXX: this doesn't test the non-forward_range code paths are constexpr.
do_test<std::span<short>, std::allocator<int>>();
+
+ // Some basic tests for overlapping ranges in constant expressions.
+ struct InputRange
+ {
+ struct Sent { const void* end; };
+
+ struct Iter
+ {
+ using value_type = int;
+ using difference_type = int;
+ constexpr explicit Iter(int* p) : ptr(p) { }
+ constexpr Iter& operator++() { ++ptr; return *this; }
+ constexpr Iter operator++(int) { auto i = *this; ++ptr; return i; }
+ constexpr int operator*() const { return *ptr; }
+ constexpr bool operator==(const Iter&) const = default;
+ constexpr bool operator==(const Sent& s) const { return ptr == s.end; }
+ int* ptr;
+ };
+
+ Iter iter;
+ Sent sent;
+
+ constexpr InputRange(int* f, int* l) : iter{f}, sent{l} { }
+ constexpr Iter begin() const { return iter; }
+ constexpr Sent end() const { return sent; }
+ };
+ static_assert( std::ranges::input_range<InputRange> );
+ static_assert( ! std::ranges::forward_range<InputRange> );
+
+ std::vector<int> vec(5);
+
+ // Test overlapping input ranges
+ vec.resize(vec.capacity());
+ vec.append_range(InputRange(vec.data(), vec.data() + 3)); // no capacity
+ vec.reserve(vec.capacity() + 2);
+ vec.append_range(InputRange(vec.data(), vec.data() + 4)); // some capacity
+ vec.reserve(vec.capacity() + 6);
+ vec.append_range(InputRange(vec.data(), vec.data() + 5)); // enough capacity
+
+ // Test overlapping forward ranges
+ vec.resize(vec.capacity());
+ vec.append_range(std::span<int>(vec)); // no capacity
+ vec.reserve(vec.size() + 2);
+ vec.append_range(std::span<int>(vec).subspan(1, 4)); // some capacity
+ vec.reserve(vec.size() + 6);
+ vec.append_range(std::span<int>(vec).subspan(1, 5)); // enough capacity
+
return true;
}
int main()
{
test_ranges();
+ test_overlapping();
static_assert( test_constexpr() );
}