diff options
author | Jonathan Wakely <jwakely@redhat.com> | 2024-10-08 21:15:18 +0100 |
---|---|---|
committer | Jonathan Wakely <redi@gcc.gnu.org> | 2024-10-27 20:09:31 +0000 |
commit | b281e13ecad12d07209924a7282c53be3a1c3774 (patch) | |
tree | 27ea80332d8c9d23ac65b212c087f5070062dc7c /libstdc++-v3 | |
parent | f1c844be5202f4be446f165d9a7625eb7ec4c5b4 (diff) | |
download | gcc-b281e13ecad12d07209924a7282c53be3a1c3774.zip gcc-b281e13ecad12d07209924a7282c53be3a1c3774.tar.gz gcc-b281e13ecad12d07209924a7282c53be3a1c3774.tar.bz2 |
libstdc++: Add P1206R7 from_range members to std::vector [PR111055]
This is another piece of P1206R7, adding new members to std::vector and
std::vector<bool>.
The __uninitialized_copy_a extension needs to be enhanced to support
passing non-common ranges (i.e. a sentinel that is a different type from
the iterator) and move-only input iterators.
libstdc++-v3/ChangeLog:
PR libstdc++/111055
* include/bits/ranges_base.h (__container_compatible_range): New
concept.
* include/bits/stl_bvector.h (vector(from_range, R&&, const Alloc&))
(assign_range, insert_range, append_range): Define.
* include/bits/stl_uninitialized.h (__do_uninit_copy): Support
non-common ranges.
(__uninitialized_copy_a): Likewise.
* include/bits/stl_vector.h (_Vector_base::_M_append_range_to):
New function.
(_Vector_base::_M_append_range): Likewise.
(vector(from_range, R&&, const Alloc&), assign_range): Define.
(append_range): Define.
(insert_range): Declare.
* include/debug/vector (vector(from_range, R&&, const Alloc&))
(assign_range, insert_range, append_range): Define.
* include/bits/vector.tcc (insert_range): Define.
* testsuite/util/testsuite_iterators.h (input_iterator_wrapper_rval):
New class template.
* testsuite/23_containers/vector/bool/cons/from_range.cc: New test.
* testsuite/23_containers/vector/bool/modifiers/assign/assign_range.cc:
New test.
* testsuite/23_containers/vector/bool/modifiers/insert/append_range.cc:
New test.
* testsuite/23_containers/vector/bool/modifiers/insert/insert_range.cc:
New test.
* testsuite/23_containers/vector/cons/from_range.cc: New test.
* testsuite/23_containers/vector/modifiers/append_range.cc: New test.
* testsuite/23_containers/vector/modifiers/assign/assign_range.cc:
New test.
* testsuite/23_containers/vector/modifiers/insert/insert_range.cc:
New test.
Reviewed-by: Patrick Palka <ppalka@redhat.com>
Diffstat (limited to 'libstdc++-v3')
15 files changed, 1366 insertions, 8 deletions
diff --git a/libstdc++-v3/include/bits/ranges_base.h b/libstdc++-v3/include/bits/ranges_base.h index cb2eba1..a2c743f 100644 --- a/libstdc++-v3/include/bits/ranges_base.h +++ b/libstdc++-v3/include/bits/ranges_base.h @@ -1079,6 +1079,16 @@ namespace ranges #if __glibcxx_ranges_to_container // C++ >= 23 struct from_range_t { explicit from_range_t() = default; }; inline constexpr from_range_t from_range{}; + +/// @cond undocumented +namespace __detail +{ + template<typename _Rg, typename _Tp> + concept __container_compatible_range + = ranges::input_range<_Rg> + && convertible_to<ranges::range_reference_t<_Rg>, _Tp>; +} +/// @endcond #endif _GLIBCXX_END_NAMESPACE_VERSION diff --git a/libstdc++-v3/include/bits/stl_bvector.h b/libstdc++-v3/include/bits/stl_bvector.h index 70f69b5..39d19e7 100644 --- a/libstdc++-v3/include/bits/stl_bvector.h +++ b/libstdc++-v3/include/bits/stl_bvector.h @@ -892,6 +892,31 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER } #endif +#if __glibcxx_ranges_to_container // C++ >= 23 + /** + * @brief Construct a vector from a range. + * @since C++23 + */ + template<__detail::__container_compatible_range<bool> _Rg> + constexpr + vector(from_range_t, _Rg&& __rg, const _Alloc& __a = _Alloc()) + : _Base(__a) + { + if constexpr (ranges::forward_range<_Rg> || ranges::sized_range<_Rg>) + { + _M_initialize(size_type(ranges::distance(__rg))); + ranges::copy(__rg, begin()); + } + else + { + auto __first = ranges::begin(__rg); + const auto __last = ranges::end(__rg); + for (; __first != __last; ++__first) + emplace_back(*__first); + } + } +#endif + _GLIBCXX20_CONSTEXPR ~vector() _GLIBCXX_NOEXCEPT { } @@ -996,6 +1021,21 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER { _M_assign_aux(__l.begin(), __l.end(), random_access_iterator_tag()); } #endif +#if __glibcxx_ranges_to_container // C++ >= 23 + /** + * @brief Assign a range to the vector. + * @since C++23 + */ + template<__detail::__container_compatible_range<bool> _Rg> + constexpr void + assign_range(_Rg&& __rg) + { + static_assert(assignable_from<bool&, ranges::range_reference_t<_Rg>>); + clear(); + append_range(std::forward<_Rg>(__rg)); + } +#endif + _GLIBCXX_NODISCARD _GLIBCXX20_CONSTEXPR iterator begin() _GLIBCXX_NOEXCEPT @@ -1279,6 +1319,86 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER { return this->insert(__p, __l.begin(), __l.end()); } #endif +#if __glibcxx_ranges_to_container // C++ >= 23 + /** + * @brief Insert a range into the vector. + * @since C++23 + */ + template<__detail::__container_compatible_range<bool> _Rg> + constexpr iterator + insert_range(const_iterator __pos, _Rg&& __rg) + { + if constexpr (ranges::forward_range<_Rg> || ranges::sized_range<_Rg>) + { + if (auto __n = size_type(ranges::distance(__rg))) + { + if (capacity() - size() >= __n) + { + std::copy_backward(__pos._M_const_cast(), end(), + this->_M_impl._M_finish + + difference_type(__n)); + auto __i = ranges::copy(__rg, __pos._M_const_cast()).out; + this->_M_impl._M_finish += difference_type(__n); + return __i; + } + else + { + const size_type __len = + _M_check_len(__n, "vector<bool>::insert_range"); + const iterator __begin = begin(), __end = end(); + _Bit_pointer __q = this->_M_allocate(__len); + iterator __start(std::__addressof(*__q), 0); + iterator __i = _M_copy_aligned(__begin, + __pos._M_const_cast(), + __start); + __i = ranges::copy(__rg, __i).out; + iterator __finish = std::copy(__pos._M_const_cast(), + __end, __i); + this->_M_deallocate(); + this->_M_impl._M_end_of_storage = __q + _S_nword(__len); + this->_M_impl._M_start = __start; + this->_M_impl._M_finish = __finish; + return __i; + } + } + else + return __pos._M_const_cast(); + } + else + return insert_range(__pos, + vector(from_range, __rg, get_allocator())); + } + + /** + * @brief Append a range at the end of the vector. + * @since C++23 + */ + template<__detail::__container_compatible_range<bool> _Rg> + constexpr void + append_range(_Rg&& __rg) + { + 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; + } + 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) + emplace_back(*__first); + if (__first != __last) + { + ranges::subrange __rest(std::move(__first), __last); + append_range(vector(from_range, __rest, get_allocator())); + } + } + } +#endif // ranges_to_container + _GLIBCXX20_CONSTEXPR void pop_back() diff --git a/libstdc++-v3/include/bits/stl_uninitialized.h b/libstdc++-v3/include/bits/stl_uninitialized.h index de3e8cb..2190261 100644 --- a/libstdc++-v3/include/bits/stl_uninitialized.h +++ b/libstdc++-v3/include/bits/stl_uninitialized.h @@ -132,10 +132,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION }; // This is the default implementation of std::uninitialized_copy. - template<typename _InputIterator, typename _ForwardIterator> + // This can be used with C++20 iterators and non-common ranges. + template<typename _InputIterator, typename _Sentinel, + typename _ForwardIterator> _GLIBCXX20_CONSTEXPR _ForwardIterator - __do_uninit_copy(_InputIterator __first, _InputIterator __last, + __do_uninit_copy(_InputIterator __first, _Sentinel __last, _ForwardIterator __result) { _UninitDestroyGuard<_ForwardIterator> __guard(__result); @@ -568,11 +570,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION // default allocator. For nondefault allocators we do not use // any of the POD optimizations. - template<typename _InputIterator, typename _ForwardIterator, - typename _Allocator> + template<typename _InputIterator, typename _Sentinel, + typename _ForwardIterator, typename _Allocator> _GLIBCXX20_CONSTEXPR _ForwardIterator - __uninitialized_copy_a(_InputIterator __first, _InputIterator __last, + __uninitialized_copy_a(_InputIterator __first, _Sentinel __last, _ForwardIterator __result, _Allocator& __alloc) { _UninitDestroyGuard<_ForwardIterator, _Allocator> @@ -586,17 +588,36 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } #if _GLIBCXX_HOSTED - template<typename _InputIterator, typename _ForwardIterator, typename _Tp> + template<typename _InputIterator, typename _Sentinel, + typename _ForwardIterator, typename _Tp> _GLIBCXX20_CONSTEXPR inline _ForwardIterator - __uninitialized_copy_a(_InputIterator __first, _InputIterator __last, + __uninitialized_copy_a(_InputIterator __first, _Sentinel __last, _ForwardIterator __result, allocator<_Tp>&) { #ifdef __cpp_lib_is_constant_evaluated if (std::is_constant_evaluated()) - return std::__do_uninit_copy(__first, __last, __result); + return std::__do_uninit_copy(std::move(__first), __last, __result); #endif + +#ifdef __glibcxx_ranges + if constexpr (!is_same_v<_InputIterator, _Sentinel>) + { + // Convert to a common range if possible, to benefit from memcpy + // optimizations that std::uninitialized_copy might use. + if constexpr (sized_sentinel_for<_Sentinel, _InputIterator> + && random_access_iterator<_InputIterator>) + return std::uninitialized_copy(__first, + __first + (__last - __first), + __result); + else // Just use default implementation. + return std::__do_uninit_copy(std::move(__first), __last, __result); + } + else + return std::uninitialized_copy(std::move(__first), __last, __result); +#else return std::uninitialized_copy(__first, __last, __result); +#endif } #endif diff --git a/libstdc++-v3/include/bits/stl_vector.h b/libstdc++-v3/include/bits/stl_vector.h index 8982ca2..df48ba3 100644 --- a/libstdc++-v3/include/bits/stl_vector.h +++ b/libstdc++-v3/include/bits/stl_vector.h @@ -65,6 +65,10 @@ #if __cplusplus >= 202002L # include <compare> #endif +#if __cplusplus > 202002L +# include <bits/ranges_algobase.h> // ranges::copy +# include <bits/ranges_util.h> // ranges::subrange +#endif #include <debug/assertions.h> @@ -399,6 +403,29 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER this->_M_impl._M_finish = this->_M_impl._M_start; this->_M_impl._M_end_of_storage = this->_M_impl._M_start + __n; } + +#if __glibcxx_ranges_to_container // C++ >= 23 + // Called by insert_range, and indirectly by assign_range, append_range. + // Initializes new elements in storage at __ptr and updates __ptr to + // point after the last new element. + // Provides strong exception safety guarantee. + // Requires [ptr, ptr+distance(rg)) is a valid range. + template<ranges::input_range _Rg> + constexpr void + _M_append_range_to(_Rg&& __rg, pointer& __ptr) + { + __ptr = std::__uninitialized_copy_a(ranges::begin(__rg), + ranges::end(__rg), + __ptr, _M_get_Tp_allocator()); + } + + // Called by assign_range, append_range, insert_range. + // Requires capacity() >= size()+distance(rg). + template<ranges::input_range _Rg> + constexpr void + _M_append_range(_Rg&& __rg) + { _M_append_range_to(std::forward<_Rg>(__rg), _M_impl._M_finish); } +#endif }; /** @@ -723,6 +750,47 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER } #endif +#if __glibcxx_ranges_to_container // C++ >= 23 + /** + * @brief Construct a vector from a range. + * @since C++23 + */ + template<__detail::__container_compatible_range<_Tp> _Rg> + constexpr + vector(from_range_t, _Rg&& __rg, const _Alloc& __a = _Alloc()) + : _Base(__a) + { + if constexpr (ranges::forward_range<_Rg> || ranges::sized_range<_Rg>) + { + const auto __n = size_type(ranges::distance(__rg)); + pointer __start = + this->_M_allocate(_S_check_init_len(__n, + _M_get_Tp_allocator())); + _Guard_alloc __guard(__start, __n, *this); + this->_M_impl._M_finish = this->_M_impl._M_start = __start; + this->_M_impl._M_end_of_storage = __start + __n; + _Base::_M_append_range(__rg); + (void) __guard._M_release(); + } + else + { + // If an exception is thrown ~_Base() will deallocate storage, + // but will not destroy elements. This RAII type destroys them. + struct _Clear + { + ~_Clear() { if (_M_this) _M_this->clear(); } + vector* _M_this; + } __guard{this}; + + auto __first = ranges::begin(__rg); + const auto __last = ranges::end(__rg); + for (; __first != __last; ++__first) + emplace_back(*__first); + __guard._M_this = nullptr; + } + } +#endif + /** * The dtor only erases the elements, and note that if the * elements themselves are pointers, the pointed-to memory is @@ -859,6 +927,62 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER } #endif +#if __glibcxx_ranges_to_container // C++ >= 23 + /** + * @brief Assign a range to the vector. + * @since C++23 + */ + template<__detail::__container_compatible_range<_Tp> _Rg> + constexpr void + assign_range(_Rg&& __rg) + { + static_assert(assignable_from<_Tp&, ranges::range_reference_t<_Rg>>); + + if constexpr (ranges::forward_range<_Rg> || ranges::sized_range<_Rg>) + { + const auto __n = size_type(ranges::distance(__rg)); + if (__n <= size()) + { + auto __res = ranges::copy(__rg, this->_M_impl._M_start); + _M_erase_at_end(__res.out); + return; + } + + reserve(__n); + auto __first = ranges::copy_n(ranges::begin(__rg), size(), + this->_M_impl._M_start).in; + [[maybe_unused]] const auto __diff = __n - size(); + _GLIBCXX_ASAN_ANNOTATE_GROW(__diff); + _Base::_M_append_range(ranges::subrange(std::move(__first), + ranges::end(__rg))); + _GLIBCXX_ASAN_ANNOTATE_GREW(__diff); + } + else // input_range<_Rg> && !sized_range<_Rg> + { + auto __first = ranges::begin(__rg); + const auto __last = ranges::end(__rg); + pointer __ptr = this->_M_impl._M_start; + pointer const __end = this->_M_impl._M_finish; + + while (__ptr < __end && __first != __last) + { + *__ptr = *__first; + ++__ptr; + ++__first; + } + + if (__first == __last) + _M_erase_at_end(__ptr); + else + { + do + emplace_back(*__first); + while (++__first != __last); + } + } + } +#endif // ranges_to_container + /// Get a copy of the memory allocation object. using _Base::get_allocator; @@ -1515,6 +1639,41 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER } #endif +#if __glibcxx_ranges_to_container // C++ >= 23 + /** + * @brief Insert a range into the vector. + * @since C++23 + */ + template<__detail::__container_compatible_range<_Tp> _Rg> + constexpr iterator + insert_range(const_iterator __pos, _Rg&& __rg); + + /** + * @brief Append a range at the end of the vector. + * @since C++23 + */ + template<__detail::__container_compatible_range<_Tp> _Rg> + constexpr void + append_range(_Rg&& __rg) + { + 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); + } + else + { + auto __first = ranges::begin(__rg); + const auto __last = ranges::end(__rg); + for (; __first != __last; ++__first) + emplace_back(*__first); + } + } +#endif // ranges_to_container + /** * @brief Remove element at given position. * @param __position Iterator pointing to element to be erased. @@ -2049,6 +2208,13 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER typename = _RequireAllocator<_Allocator>> vector(_InputIterator, _InputIterator, _Allocator = _Allocator()) -> vector<_ValT, _Allocator>; + +#if __glibcxx_ranges_to_container // C++ >= 23 + template<ranges::input_range _Rg, + typename _Alloc = allocator<ranges::range_value_t<_Rg>>> + vector(from_range_t, _Rg&&, _Alloc = _Alloc()) + -> vector<ranges::range_value_t<_Rg>, _Alloc>; +#endif #endif /** diff --git a/libstdc++-v3/include/bits/vector.tcc b/libstdc++-v3/include/bits/vector.tcc index a99a5b5..542a661 100644 --- a/libstdc++-v3/include/bits/vector.tcc +++ b/libstdc++-v3/include/bits/vector.tcc @@ -974,6 +974,129 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER } } +#if __glibcxx_ranges_to_container // C++ >= 23 + template<typename _Tp, typename _Alloc> + template<__detail::__container_compatible_range<_Tp> _Rg> + constexpr auto + vector<_Tp, _Alloc>:: + insert_range(const_iterator __pos, _Rg&& __rg) + -> iterator + { + if (__pos == cend()) + { + append_range(std::forward<_Rg>(__rg)); + return end(); + } + + if constexpr (ranges::forward_range<_Rg>) + { + // Start of existing elements: + pointer __old_start = this->_M_impl._M_start; + // End of existing elements: + pointer __old_finish = this->_M_impl._M_finish; + // Insertion point: + const auto __ins_idx = __pos - cbegin(); + pointer __ins = __old_start + __ins_idx; + // Number of new elements to insert: + const auto __n = size_type(ranges::distance(__rg)); + // Number of elements that can fit in unused capacity: + const auto __cap = this->_M_impl._M_end_of_storage - __old_finish; + if (__cap >= __n) + { + // Number of existing elements after insertion point: + const size_type __elems_after = cend() - __pos; + if (__elems_after > __n) + { + _GLIBCXX_ASAN_ANNOTATE_GROW(__n); + std::__uninitialized_move_a(__old_finish - __n, + __old_finish, + __old_finish, + _M_get_Tp_allocator()); + this->_M_impl._M_finish += __n; + _GLIBCXX_ASAN_ANNOTATE_GREW(__n); + std::move_backward(__ins, __old_finish - __n, __old_finish); + ranges::copy(__rg, __ins); + } + else + { + auto __first = ranges::begin(__rg); + const auto __last = ranges::end(__rg); + auto __mid = ranges::next(__first, __elems_after); + _GLIBCXX_ASAN_ANNOTATE_GROW(__n); + _Base::_M_append_range(ranges::subrange(__mid, __last)); + _GLIBCXX_ASAN_ANNOTATE_GREW(__n - __elems_after); + std::__uninitialized_move_a(__ins, __old_finish, + this->_M_impl._M_finish, + _M_get_Tp_allocator()); + this->_M_impl._M_finish += __elems_after; + _GLIBCXX_ASAN_ANNOTATE_GREW(__elems_after); + ranges::copy(__first, __mid, __ins); + } + } + else // Reallocate + { + const size_type __len + = _M_check_len(__n, "vector::insert_range"); + + struct _Guard : _Guard_alloc + { + // End of elements to destroy: + pointer _M_finish = _Guard_alloc::_M_storage; + + using _Guard_alloc::_Guard_alloc; + + constexpr + ~_Guard() + { + std::_Destroy(this->_M_storage, _M_finish, + this->_M_vect._M_get_Tp_allocator()); + } + }; + + // Allocate new storage: + pointer __new_start(this->_M_allocate(__len)); + _Guard __guard(__new_start, __len, *this); + + auto& __alloc = _M_get_Tp_allocator(); + + // Populate the new storage in three steps. After each step, + // __guard owns the new storage and any elements that have + // been constructed there. + + // Move elements from before insertion point to new storage: + __guard._M_finish + = std::__uninitialized_move_if_noexcept_a( + __old_start, __ins, __new_start, __alloc); + + // Append new elements to new storage: + _Base::_M_append_range_to(__rg, __guard._M_finish); + + // Move elements from after insertion point to new storage: + __guard._M_finish + = std::__uninitialized_move_if_noexcept_a( + __ins, __old_finish, __guard._M_finish, __alloc); + + _GLIBCXX_ASAN_ANNOTATE_REINIT; // Creates _Asan::_Reinit. + + // All elements are in the new storage, exchange ownership + // with __guard so that it cleans up the old storage: + this->_M_impl._M_start = __guard._M_storage; + this->_M_impl._M_finish = __guard._M_finish; + this->_M_impl._M_end_of_storage = __new_start + __len; + __guard._M_storage = __old_start; + __guard._M_finish = __old_finish; + __guard._M_len = (__old_finish - __old_start) + __cap; + // _Asan::_Reinit destructor marks unused capacity. + // _Guard destructor destroys [old_start,old_finish). + // _Guard_alloc destructor frees [old_start,old_start+len). + } + return begin() + __ins_idx; + } + else + return insert_range(__pos, vector(from_range, std::move(__rg), + _M_get_Tp_allocator())); + } +#endif // ranges_to_container // vector<bool> template<typename _Alloc> diff --git a/libstdc++-v3/include/debug/vector b/libstdc++-v3/include/debug/vector index fe43a37..f2ab4f7 100644 --- a/libstdc++-v3/include/debug/vector +++ b/libstdc++-v3/include/debug/vector @@ -244,6 +244,19 @@ namespace __debug const allocator_type& __a = allocator_type()) : _Base(__l, __a) { } +#if __glibcxx_ranges_to_container // C++ >= 23 + /** + * @brief Construct a vector from a range. + * @since C++23 + */ + template<std::__detail::__container_compatible_range<_Tp> _Rg> + constexpr + vector(std::from_range_t __t, _Rg&& __rg, + const allocator_type& __a = allocator_type()) + : _Base(__t, std::forward<_Rg>(__rg), __a) + { } +#endif + ~vector() = default; #endif @@ -858,6 +871,56 @@ namespace __debug const _Base& _M_base() const _GLIBCXX_NOEXCEPT { return *this; } +#if __glibcxx_ranges_to_container // C++ >= 23 + template<std::__detail::__container_compatible_range<_Tp> _Rg> + constexpr void + assign_range(_Rg&& __rg) + { + auto __old_begin = _Base::begin(); + auto __old_size = _Base::size(); + _Base::assign_range(__rg); + if (!std::__is_constant_evaluated()) + { + if (_Base::begin() != __old_begin) + this->_M_invalidate_all(); + else if (_Base::size() < __old_size) + this->_M_invalidate_after_nth(_Base::size()); + this->_M_update_guaranteed_capacity(); + } + } + + template<__detail::__container_compatible_range<_Tp> _Rg> + constexpr iterator + insert_range(const_iterator __pos, _Rg&& __rg) + { + auto __old_begin = _Base::begin(); + auto __old_size = _Base::size(); + auto __res = _Base::insert_range(__pos.base(), __rg); + if (!std::__is_constant_evaluated()) + { + if (_Base::begin() != __old_begin) + this->_M_invalidate_all(); + this->_M_update_guaranteed_capacity(); + } + return iterator(__res, this); + } + + template<__detail::__container_compatible_range<_Tp> _Rg> + constexpr void + append_range(_Rg&& __rg) + { + auto __old_begin = _Base::begin(); + auto __old_size = _Base::size(); + _Base::append_range(__rg); + if (!std::__is_constant_evaluated()) + { + if (_Base::begin() != __old_begin) + this->_M_invalidate_all(); + this->_M_update_guaranteed_capacity(); + } + } +#endif + private: void _M_invalidate_after_nth(difference_type __n) _GLIBCXX_NOEXCEPT @@ -937,6 +1000,13 @@ namespace __debug typename = _RequireAllocator<_Allocator>> vector(size_t, _Tp, _Allocator = _Allocator()) -> vector<_Tp, _Allocator>; + +#if __glibcxx_ranges_to_container // C++ >= 23 + template<ranges::input_range _Rg, + typename _Alloc = allocator<ranges::range_value_t<_Rg>>> + vector(from_range_t, _Rg&&, _Alloc = _Alloc()) + -> vector<ranges::range_value_t<_Rg>, _Alloc>; +#endif #endif } // namespace __debug diff --git a/libstdc++-v3/testsuite/23_containers/vector/bool/cons/from_range.cc b/libstdc++-v3/testsuite/23_containers/vector/bool/cons/from_range.cc new file mode 100644 index 0000000..f5180e5 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/vector/bool/cons/from_range.cc @@ -0,0 +1,91 @@ +// { dg-do compile { target c++23 } } + +#include <vector> +#include <span> +#include <testsuite_hooks.h> +#include <testsuite_iterators.h> +#include <testsuite_allocator.h> + +template<typename Range, typename Alloc> +constexpr void +do_test(Alloc alloc) +{ + using T = std::ranges::range_value_t<Range>; + T a[]{1,1,0,1,0,0,1,0,0}; + + auto eq = [](const std::vector<bool, Alloc>& l, std::span<T> r) { + if (l.size() != r.size()) + return false; + for (auto i = 0u; i < l.size(); ++i) + if (l[i] != r[i]) + return false; + return true; + }; + + std::vector<bool, Alloc> v0(std::from_range, Range(a, a+0)); + VERIFY( v0.empty() ); + VERIFY( v0.get_allocator() == Alloc() ); + + std::vector<bool, Alloc> v4(std::from_range, Range(a, a+4)); + VERIFY( eq(v4, {a, 4}) ); + VERIFY( v4.get_allocator() == Alloc() ); + + std::vector<bool, Alloc> v9(std::from_range, Range(a, a+9), alloc); + VERIFY( eq(v9, {a, 9}) ); + VERIFY( v9.get_allocator() == alloc ); +} + +template<typename Range> +void +do_test_a() +{ + do_test<Range>(std::allocator<bool>()); + do_test<Range>(__gnu_test::uneq_allocator<bool>(42)); +} + +bool +test_ranges() +{ + using namespace __gnu_test; + + do_test_a<test_forward_range<bool>>(); + do_test_a<test_forward_sized_range<bool>>(); + do_test_a<test_sized_range_sized_sent<bool, forward_iterator_wrapper>>(); + + do_test_a<test_input_range<bool>>(); + do_test_a<test_input_sized_range<bool>>(); + do_test_a<test_sized_range_sized_sent<bool, input_iterator_wrapper>>(); + + do_test_a<test_range<bool, input_iterator_wrapper_nocopy>>(); + do_test_a<test_sized_range<bool, input_iterator_wrapper_nocopy>>(); + do_test_a<test_sized_range_sized_sent<bool, input_iterator_wrapper_nocopy>>(); + + do_test_a<test_forward_range<short>>(); + do_test_a<test_input_range<short>>(); + + // Not lvalue-convertible to bool + struct C { + C(bool v) : val(v) { } + operator bool() && { return val; } + bool operator==(bool b) const { return b == val; } + bool val; + }; + using rvalue_input_range = test_range<C, input_iterator_wrapper_rval>; + do_test_a<rvalue_input_range>(); + + return true; +} + +constexpr bool +test_constexpr() +{ + // XXX: this doesn't test the non-forward_range code paths are constexpr. + do_test<std::span<bool>, std::allocator<bool>>; + return true; +} + +int main() +{ + test_ranges(); + static_assert( test_constexpr() ); +} diff --git a/libstdc++-v3/testsuite/23_containers/vector/bool/modifiers/assign/assign_range.cc b/libstdc++-v3/testsuite/23_containers/vector/bool/modifiers/assign/assign_range.cc new file mode 100644 index 0000000..a014dfe --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/vector/bool/modifiers/assign/assign_range.cc @@ -0,0 +1,106 @@ +// { dg-do compile { target c++23 } } + +#include <vector> +#include <span> +#include <testsuite_hooks.h> +#include <testsuite_iterators.h> +#include <testsuite_allocator.h> + +template<typename Range, typename Alloc> +constexpr void +do_test() +{ + using T = std::ranges::range_value_t<Range>; + T a[]{1,1,0,1,0,0,1,0,0}; + + auto eq = [](const std::vector<bool, Alloc>& l, std::span<T> r) { + if (l.size() != r.size()) + return false; + for (auto i = 0u; i < l.size(); ++i) + if (l[i] != r[i]) + return false; + return true; + }; + + Range r4(a, a+4); + Range r9(a); + + std::vector<bool, Alloc> v; + v.assign_range(Range(a, a)); + VERIFY( v.empty() ); + VERIFY( v.capacity() == 0 ); + v.assign_range(r4); + VERIFY( eq(v, {a, 4}) ); + v.clear(); + v.assign_range(r9); // larger than v.capacity() + VERIFY( eq(v, a) ); + v.assign_range(r9); // equal to size() and equal to capacity() + VERIFY( eq(v, a) ); + v.resize(1); + v.assign_range(r4); // larger than size(), smaller than capacity() + VERIFY( eq(v, {a, 4}) ); + v.clear(); + v.resize(4); + v.assign_range(r4); // equal to size(), smaller than capacity() + VERIFY( eq(v, {a, 4}) ); + v.shrink_to_fit(); + v.assign_range(r9); // larger than capacity() + VERIFY( eq(v, a) ); + v.assign_range(Range(a, a)); + VERIFY( v.empty() ); +} + +template<typename Range> +void +do_test_a() +{ + do_test<Range, std::allocator<bool>>(); + do_test<Range, __gnu_test::SimpleAllocator<bool>>(); +} + +bool +test_ranges() +{ + using namespace __gnu_test; + + do_test_a<test_forward_range<bool>>(); + do_test_a<test_forward_sized_range<bool>>(); + do_test_a<test_sized_range_sized_sent<bool, forward_iterator_wrapper>>(); + + do_test_a<test_input_range<bool>>(); + do_test_a<test_input_sized_range<bool>>(); + do_test_a<test_sized_range_sized_sent<bool, input_iterator_wrapper>>(); + + do_test_a<test_range<bool, input_iterator_wrapper_nocopy>>(); + do_test_a<test_sized_range<bool, input_iterator_wrapper_nocopy>>(); + do_test_a<test_sized_range_sized_sent<bool, input_iterator_wrapper_nocopy>>(); + + do_test_a<test_forward_range<short>>(); + do_test_a<test_input_range<short>>(); + + // Not lvalue-convertible to bool + struct C { + C(bool v) : val(v) { } + operator bool() && { return val; } + bool operator==(bool b) const { return b == val; } + bool val; + }; + using rvalue_input_range = test_range<C, input_iterator_wrapper_rval>; + do_test_a<rvalue_input_range>(); + + return true; +} + +constexpr bool +test_constexpr() +{ + // XXX: this doesn't test the non-forward_range code paths are constexpr. + do_test<std::span<short>, std::allocator<bool>>; + return true; +} + +int main() +{ + test_ranges(); + static_assert( test_constexpr() ); +} diff --git a/libstdc++-v3/testsuite/23_containers/vector/bool/modifiers/insert/append_range.cc b/libstdc++-v3/testsuite/23_containers/vector/bool/modifiers/insert/append_range.cc new file mode 100644 index 0000000..e811a16 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/vector/bool/modifiers/insert/append_range.cc @@ -0,0 +1,93 @@ +// { dg-do compile { target c++23 } } + +#include <vector> +#include <span> +#include <testsuite_hooks.h> +#include <testsuite_iterators.h> +#include <testsuite_allocator.h> + +template<typename Range, typename Alloc> +constexpr void +do_test() +{ + using T = std::ranges::range_value_t<Range>; + T a[]{1,1,0,1,0,0,1,0,0}; + + auto eq = [](const std::vector<bool, Alloc>& l, std::span<T> r) { + if (l.size() != r.size()) + return false; + for (auto i = 0u; i < l.size(); ++i) + if (l[i] != r[i]) + return false; + return true; + }; + + Range r4(a, a+4); + Range r5(a+4, a+9); + + std::vector<bool, Alloc> v; + v.append_range(r4); + VERIFY( eq(v, {a, 4}) ); + v.append_range(r5); // larger than v.capacity() + VERIFY( eq(v, a) ); + v.append_range(Range(a, a)); + VERIFY( eq(v, a) ); + v.clear(); + v.append_range(Range(a, a)); + VERIFY( v.empty() ); +} + +template<typename Range> +void +do_test_a() +{ + do_test<Range, std::allocator<bool>>(); + do_test<Range, __gnu_test::SimpleAllocator<bool>>(); +} + +bool +test_ranges() +{ + using namespace __gnu_test; + + do_test_a<test_forward_range<bool>>(); + do_test_a<test_forward_sized_range<bool>>(); + do_test_a<test_sized_range_sized_sent<bool, forward_iterator_wrapper>>(); + + do_test_a<test_input_range<bool>>(); + do_test_a<test_input_sized_range<bool>>(); + do_test_a<test_sized_range_sized_sent<bool, input_iterator_wrapper>>(); + + do_test_a<test_range<bool, input_iterator_wrapper_nocopy>>(); + do_test_a<test_sized_range<bool, input_iterator_wrapper_nocopy>>(); + do_test_a<test_sized_range_sized_sent<bool, input_iterator_wrapper_nocopy>>(); + + do_test_a<test_forward_range<short>>(); + do_test_a<test_input_range<short>>(); + + // Not lvalue-convertible to bool + struct C { + C(bool v) : val(v) { } + operator bool() && { return val; } + bool operator==(bool b) const { return b == val; } + bool val; + }; + using rvalue_input_range = test_range<C, input_iterator_wrapper_rval>; + do_test_a<rvalue_input_range>(); + + return true; +} + +constexpr bool +test_constexpr() +{ + // XXX: this doesn't test the non-forward_range code paths are constexpr. + do_test<std::span<short>, std::allocator<bool>>; + return true; +} + +int main() +{ + test_ranges(); + static_assert( test_constexpr() ); +} diff --git a/libstdc++-v3/testsuite/23_containers/vector/bool/modifiers/insert/insert_range.cc b/libstdc++-v3/testsuite/23_containers/vector/bool/modifiers/insert/insert_range.cc new file mode 100644 index 0000000..82b67cd --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/vector/bool/modifiers/insert/insert_range.cc @@ -0,0 +1,104 @@ +// { dg-do compile { target c++23 } } + +#include <vector> +#include <span> +#include <testsuite_hooks.h> +#include <testsuite_iterators.h> +#include <testsuite_allocator.h> + +template<typename Range, typename Alloc> +constexpr void +do_test() +{ + using T = std::ranges::range_value_t<Range>; + T a[]{1,1,0,1,0,0,1,0,0}; + + auto eq = [](const std::vector<bool, Alloc>& l, std::span<T> r) { + if (l.size() != r.size()) + return false; + for (auto i = 0u; i < l.size(); ++i) + if (l[i] != r[i]) + return false; + return true; + }; + + std::vector<bool, Alloc> v; + v.insert_range(v.begin(), Range(a, a+0)); + VERIFY( v.empty() ); + VERIFY( v.capacity() == 0 ); + v.insert_range(v.begin(), Range(a, a+4)); + VERIFY( eq(v, {a, a+4}) ); + v.clear(); + v.insert_range(v.begin(), Range(a, a+5)); + VERIFY( eq(v, {a+4, a+9}) ); + v.insert_range(v.begin(), Range(a, a+4)); + VERIFY( eq(v, a) ); + v.clear(); + v.shrink_to_fit(); + v.insert_range(v.begin(), Range(a, a+3)); + v.insert_range(v.end(), Range(a+6, a+9)); + v.insert_range(v.begin()+3, Range(a+3, a+6)); + VERIFY( eq(v, a) ); + v.resize(3); + v.insert_range(v.begin()+1, Range(a+4, a+9)); + v.insert_range(v.begin()+1, Range(a+1, a+3)); + v.resize(9); + VERIFY( eq(v, a) ); + v.insert_range(v.begin(), Range(a, a)); + VERIFY( eq(v, a) ); +} + +template<typename Range> +void +do_test_a() +{ + do_test<Range, std::allocator<bool>>(); + do_test<Range, __gnu_test::SimpleAllocator<bool>>(); +} + +bool +test_ranges() +{ + using namespace __gnu_test; + + do_test_a<test_forward_range<bool>>(); + do_test_a<test_forward_sized_range<bool>>(); + do_test_a<test_sized_range_sized_sent<bool, forward_iterator_wrapper>>(); + + do_test_a<test_input_range<bool>>(); + do_test_a<test_input_sized_range<bool>>(); + do_test_a<test_sized_range_sized_sent<bool, input_iterator_wrapper>>(); + + do_test_a<test_range<bool, input_iterator_wrapper_nocopy>>(); + do_test_a<test_sized_range<bool, input_iterator_wrapper_nocopy>>(); + do_test_a<test_sized_range_sized_sent<bool, input_iterator_wrapper_nocopy>>(); + + do_test_a<test_forward_range<short>>(); + do_test_a<test_input_range<short>>(); + + // Not lvalue-convertible to bool + struct C { + C(bool v) : val(v) { } + operator bool() && { return val; } + bool operator==(bool b) const { return b == val; } + bool val; + }; + using rvalue_input_range = test_range<C, input_iterator_wrapper_rval>; + do_test_a<rvalue_input_range>(); + + return true; +} + +constexpr bool +test_constexpr() +{ + // XXX: this doesn't test the non-forward_range code paths are constexpr. + do_test<std::span<bool>, std::allocator<bool>>; + return true; +} + +int main() +{ + test_ranges(); + static_assert( test_constexpr() ); +} diff --git a/libstdc++-v3/testsuite/23_containers/vector/cons/from_range.cc b/libstdc++-v3/testsuite/23_containers/vector/cons/from_range.cc new file mode 100644 index 0000000..d709c77 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/vector/cons/from_range.cc @@ -0,0 +1,108 @@ +// { dg-do compile { target c++23 } } + +#include <vector> +#include <span> +#include <testsuite_hooks.h> +#include <testsuite_iterators.h> +#include <testsuite_allocator.h> + +void +test_deduction_guide(long* p) +{ + __gnu_test::test_input_range<long> r(p, p); + std::vector v(std::from_range, r); + static_assert(std::is_same_v<decltype(v), std::vector<long>>); + + using Alloc = __gnu_test::SimpleAllocator<long>; + Alloc alloc; + std::vector v2(std::from_range, r, alloc); + static_assert(std::is_same_v<decltype(v2), std::vector<long, Alloc>>); +} + +template<typename Range, typename Alloc> +constexpr void +do_test(Alloc alloc) +{ + // The vector's value_type. + using V = typename std::allocator_traits<Alloc>::value_type; + + // The range's value_type. + using T = std::ranges::range_value_t<Range>; + T a[]{1,2,3,4,5,6,7,8,9}; + + auto eq = [](const std::vector<V, Alloc>& l, std::span<T> r) { + if (l.size() != r.size()) + return false; + for (auto i = 0u; i < l.size(); ++i) + if (l[i] != r[i]) + return false; + return true; + }; + + std::vector<V, Alloc> v0(std::from_range, Range(a, a+0)); + VERIFY( v0.empty() ); + VERIFY( v0.get_allocator() == Alloc() ); + + std::vector<V, Alloc> v4(std::from_range, Range(a, a+4)); + VERIFY( eq(v4, {a, 4}) ); + VERIFY( v4.get_allocator() == Alloc() ); + + std::vector<V, Alloc> v9(std::from_range, Range(a, a+9), alloc); + VERIFY( eq(v9, {a, 9}) ); + VERIFY( v9.get_allocator() == alloc ); +} + +template<typename Range> +void +do_test_a() +{ + do_test<Range>(std::allocator<int>()); + do_test<Range>(__gnu_test::uneq_allocator<int>(42)); +} + +bool +test_ranges() +{ + using namespace __gnu_test; + + do_test_a<test_forward_range<int>>(); + do_test_a<test_forward_sized_range<int>>(); + do_test_a<test_sized_range_sized_sent<int, forward_iterator_wrapper>>(); + + do_test_a<test_input_range<int>>(); + do_test_a<test_input_sized_range<int>>(); + do_test_a<test_sized_range_sized_sent<int, input_iterator_wrapper>>(); + + do_test_a<test_range<int, input_iterator_wrapper_nocopy>>(); + do_test_a<test_sized_range<int, input_iterator_wrapper_nocopy>>(); + do_test_a<test_sized_range_sized_sent<int, input_iterator_wrapper_nocopy>>(); + + do_test_a<test_forward_range<short>>(); + do_test_a<test_input_range<short>>(); + + // Not lvalue-convertible to bool + struct C { + C(int v) : val(v) { } + operator int() && { return val; } + bool operator==(int b) const { return b == val; } + int val; + }; + using rvalue_input_range = test_range<C, input_iterator_wrapper_rval>; + do_test<rvalue_input_range, std::allocator<int>>(); + + return true; +} + +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>>; + return true; +} + +int main() +{ + test_ranges(); + static_assert( test_constexpr() ); +} diff --git a/libstdc++-v3/testsuite/23_containers/vector/modifiers/append_range.cc b/libstdc++-v3/testsuite/23_containers/vector/modifiers/append_range.cc new file mode 100644 index 0000000..ff86cb7 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/vector/modifiers/append_range.cc @@ -0,0 +1,97 @@ +// { dg-do compile { target c++23 } } + +#include <vector> +#include <span> +#include <testsuite_hooks.h> +#include <testsuite_iterators.h> +#include <testsuite_allocator.h> + +template<typename Range, typename Alloc> +constexpr void +do_test() +{ + // The vector's value_type. + using V = typename std::allocator_traits<Alloc>::value_type; + + // The range's value_type. + using T = std::ranges::range_value_t<Range>; + T a[]{1,2,3,4,5,6,7,8,9}; + + auto eq = [](const std::vector<V, Alloc>& l, std::span<T> r) { + if (l.size() != r.size()) + return false; + for (auto i = 0u; i < l.size(); ++i) + if (l[i] != r[i]) + return false; + return true; + }; + + Range r4(a, a+4); + Range r5(a+4, a+9); + + std::vector<V, Alloc> v; + v.append_range(r4); + VERIFY( eq(v, {a, 4}) ); + v.append_range(r5); // larger than v.capacity() + VERIFY( eq(v, a) ); + v.append_range(Range(a, a)); + VERIFY( eq(v, a) ); + v.clear(); + v.append_range(Range(a, a)); + VERIFY( v.empty() ); +} + +template<typename Range> +void +do_test_a() +{ + do_test<Range, std::allocator<int>>(); + do_test<Range, __gnu_test::SimpleAllocator<int>>(); +} + +bool +test_ranges() +{ + using namespace __gnu_test; + + do_test_a<test_forward_range<int>>(); + do_test_a<test_forward_sized_range<int>>(); + do_test_a<test_sized_range_sized_sent<int, forward_iterator_wrapper>>(); + + do_test_a<test_input_range<int>>(); + do_test_a<test_input_sized_range<int>>(); + do_test_a<test_sized_range_sized_sent<int, input_iterator_wrapper>>(); + + do_test_a<test_range<int, input_iterator_wrapper_nocopy>>(); + do_test_a<test_sized_range<int, input_iterator_wrapper_nocopy>>(); + do_test_a<test_sized_range_sized_sent<int, input_iterator_wrapper_nocopy>>(); + + do_test_a<test_forward_range<short>>(); + do_test_a<test_input_range<short>>(); + + // Not lvalue-convertible to bool + struct C { + C(int v) : val(v) { } + operator int() && { return val; } + bool operator==(int b) const { return b == val; } + int val; + }; + using rvalue_input_range = test_range<C, input_iterator_wrapper_rval>; + do_test<rvalue_input_range, std::allocator<int>>(); + + return true; +} + +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>>; + return true; +} + +int main() +{ + test_ranges(); + static_assert( test_constexpr() ); +} diff --git a/libstdc++-v3/testsuite/23_containers/vector/modifiers/assign/assign_range.cc b/libstdc++-v3/testsuite/23_containers/vector/modifiers/assign/assign_range.cc new file mode 100644 index 0000000..c3302e9 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/vector/modifiers/assign/assign_range.cc @@ -0,0 +1,121 @@ +// { dg-do compile { target c++23 } } + +#include <vector> +#include <span> +#include <testsuite_hooks.h> +#include <testsuite_iterators.h> +#include <testsuite_allocator.h> + +template<typename Range, typename Alloc> +constexpr void +do_test() +{ + // The vector's value_type. + using V = typename std::allocator_traits<Alloc>::value_type; + + // The range's value_type. + using T = std::ranges::range_value_t<Range>; + T a[]{1,2,3,4,5,6,7,8,9}; + + auto eq = [](const std::vector<V, Alloc>& l, std::span<T> r) { + if (l.size() != r.size()) + return false; + for (auto i = 0u; i < l.size(); ++i) + if (l[i] != r[i]) + return false; + return true; + }; + + Range r4(a, a+4); + Range r9(a); + + // assign to empty vector + std::vector<V, Alloc> v; + v.assign_range(Range(a, a)); + VERIFY( v.empty() ); + VERIFY( v.capacity() == 0 ); + v.assign_range(r4); + VERIFY( eq(v, {a, 4}) ); + v.clear(); + v.assign_range(r9); // larger than v.capacity() + VERIFY( eq(v, a) ); + v.clear(); + v.assign_range(r4); // smaller than v.capacity() + VERIFY( eq(v, {a, 4}) ); + v.clear(); + v.assign_range(r9); // equal to v.capacity() + VERIFY( eq(v, a) ); + + // assign to non-empty vector + v.assign_range(r4); // smaller than size() + VERIFY( eq(v, {a, 4}) ); + v.assign_range(r9); // larger than size(), equal to capacity() + VERIFY( eq(v, a) ); + v.resize(1); + v.assign_range(r4); // larger than size(), smaller than capacity() + VERIFY( eq(v, {a, 4}) ); + v.clear(); + v.resize(4); + v.assign_range(r4); // equal to size(), smaller than capacity() + VERIFY( eq(v, {a, 4}) ); + v.shrink_to_fit(); + v.assign_range(r9); // larger than capacity() + VERIFY( eq(v, a) ); + v.assign_range(Range(a, a)); + VERIFY( v.empty() ); +} + +template<typename Range> +void +do_test_a() +{ + do_test<Range, std::allocator<int>>(); + do_test<Range, __gnu_test::SimpleAllocator<int>>(); +} + +bool +test_ranges() +{ + using namespace __gnu_test; + + do_test_a<test_forward_range<int>>(); + do_test_a<test_forward_sized_range<int>>(); + do_test_a<test_sized_range_sized_sent<int, forward_iterator_wrapper>>(); + + do_test_a<test_input_range<int>>(); + do_test_a<test_input_sized_range<int>>(); + do_test_a<test_sized_range_sized_sent<int, input_iterator_wrapper>>(); + + do_test_a<test_range<int, input_iterator_wrapper_nocopy>>(); + do_test_a<test_sized_range<int, input_iterator_wrapper_nocopy>>(); + do_test_a<test_sized_range_sized_sent<int, input_iterator_wrapper_nocopy>>(); + + do_test_a<test_forward_range<short>>(); + do_test_a<test_input_range<short>>(); + + // Not lvalue-convertible to bool + struct C { + C(int v) : val(v) { } + operator int() && { return val; } + bool operator==(int b) const { return b == val; } + int val; + }; + using rvalue_input_range = test_range<C, input_iterator_wrapper_rval>; + do_test<rvalue_input_range, std::allocator<int>>(); + + return true; +} + +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>>; + return true; +} + +int main() +{ + test_ranges(); + static_assert( test_constexpr() ); +} diff --git a/libstdc++-v3/testsuite/23_containers/vector/modifiers/insert/insert_range.cc b/libstdc++-v3/testsuite/23_containers/vector/modifiers/insert/insert_range.cc new file mode 100644 index 0000000..4cfab10 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/vector/modifiers/insert/insert_range.cc @@ -0,0 +1,108 @@ +// { dg-do compile { target c++23 } } + +#include <vector> +#include <span> +#include <testsuite_hooks.h> +#include <testsuite_iterators.h> +#include <testsuite_allocator.h> + +template<typename Range, typename Alloc> +constexpr void +do_test() +{ + // The vector's value_type. + using V = typename std::allocator_traits<Alloc>::value_type; + + // The range's value_type. + using T = std::ranges::range_value_t<Range>; + T a[]{1,2,3,4,5,6,7,8,9}; + + auto eq = [](const std::vector<V, Alloc>& l, std::span<T> r) { + if (l.size() != r.size()) + return false; + for (auto i = 0u; i < l.size(); ++i) + if (l[i] != r[i]) + return false; + return true; + }; + + std::vector<V, Alloc> v; + v.insert_range(v.begin(), Range(a, a)); + VERIFY( v.empty() ); + VERIFY( v.capacity() == 0 ); + v.insert_range(v.begin(), Range(a, a+4)); + VERIFY( eq(v, {a, a+4}) ); + v.clear(); + v.insert_range(v.begin(), Range(a, a+5)); + VERIFY( eq(v, {a+4, a+9}) ); + v.insert_range(v.begin(), Range(a, a+4)); + VERIFY( eq(v, a) ); + v.clear(); + v.shrink_to_fit(); + v.insert_range(v.begin(), Range(a, a+3)); + v.insert_range(v.end(), Range(a+6, a+9)); + v.insert_range(v.begin()+3, Range(a+3, a+6)); + VERIFY( eq(v, a) ); + v.resize(3); + v.insert_range(v.begin()+1, Range(a+4, a+9)); + v.insert_range(v.begin()+1, Range(a+1, a+3)); + v.resize(9); + VERIFY( eq(v, a) ); + v.insert_range(v.begin() + 6, Range(a, a)); + VERIFY( eq(v, a) ); +} + +template<typename Range> +void +do_test_a() +{ + do_test<Range, std::allocator<int>>(); + do_test<Range, __gnu_test::SimpleAllocator<int>>(); +} + +bool +test_ranges() +{ + using namespace __gnu_test; + + do_test_a<test_forward_range<int>>(); + do_test_a<test_forward_sized_range<int>>(); + do_test_a<test_sized_range_sized_sent<int, forward_iterator_wrapper>>(); + + do_test_a<test_input_range<int>>(); + do_test_a<test_input_sized_range<int>>(); + do_test_a<test_sized_range_sized_sent<int, input_iterator_wrapper>>(); + + do_test_a<test_range<int, input_iterator_wrapper_nocopy>>(); + do_test_a<test_sized_range<int, input_iterator_wrapper_nocopy>>(); + do_test_a<test_sized_range_sized_sent<int, input_iterator_wrapper_nocopy>>(); + + do_test_a<test_forward_range<short>>(); + do_test_a<test_input_range<short>>(); + + // Not lvalue-convertible to bool + struct C { + C(int v) : val(v) { } + operator int() && { return val; } + bool operator==(int b) const { return b == val; } + int val; + }; + using rvalue_input_range = test_range<C, input_iterator_wrapper_rval>; + do_test<rvalue_input_range, std::allocator<int>>(); + + return true; +} + +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>>; + return true; +} + +int main() +{ + test_ranges(); + static_assert( test_constexpr() ); +} diff --git a/libstdc++-v3/testsuite/util/testsuite_iterators.h b/libstdc++-v3/testsuite/util/testsuite_iterators.h index e7f7abe..aafafa7 100644 --- a/libstdc++-v3/testsuite/util/testsuite_iterators.h +++ b/libstdc++-v3/testsuite/util/testsuite_iterators.h @@ -783,6 +783,26 @@ namespace __gnu_test } }; + // An input iterator type with an rvalue reference type. + template<typename T> + struct input_iterator_wrapper_rval : input_iterator_wrapper<T> + { + using input_iterator_wrapper<T>::input_iterator_wrapper; + + using input_iterator_wrapper<T>::operator++; + + input_iterator_wrapper_rval& + operator++() + { + input_iterator_wrapper<T>::operator++(); + return *this; + } + + T&& + operator*() const + { return std::move(input_iterator_wrapper<T>::operator*()); } + }; + // A type meeting the minimum std::range requirements template<typename T, template<typename> class Iter> class test_range |