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>
This commit is contained in:
parent
f1c844be52
commit
b281e13eca
15 changed files with 1366 additions and 8 deletions
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
/**
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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() );
|
||||
}
|
|
@ -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() );
|
||||
}
|
|
@ -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() );
|
||||
}
|
|
@ -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() );
|
||||
}
|
108
libstdc++-v3/testsuite/23_containers/vector/cons/from_range.cc
Normal file
108
libstdc++-v3/testsuite/23_containers/vector/cons/from_range.cc
Normal file
|
@ -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() );
|
||||
}
|
|
@ -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() );
|
||||
}
|
|
@ -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() );
|
||||
}
|
|
@ -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() );
|
||||
}
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue