On Fri, 21 Mar 2025 at 14:31, Tomasz Kaminski <tkami...@redhat.com> wrote:
>
>
>
> On Fri, Mar 21, 2025 at 11:14 AM Tomasz Kamiński <tkami...@redhat.com> wrote:
>>
>> This is another piece of P1206R7, adding from_range constructor, 
>> append_range,
>> prepend_range, insert_range, and assign_range members to std::deque.
>>
>> For insert_range, the handling of insertion in the middle of input-only 
>> ranges
>> that are sized could be optimized, we still insert nodes one-by-one in such 
>> case.
>> For forward and stronger ranges, we reduce them to common_range case,
>> by computing the iterator when computing the distance.
>> This is slightly suboptimal, as it require range to be iterated for 
>> non-common
>> forward ranges that are sized.
>>
>> This patch extract a set of  helper functions that accepts (iterator, 
>> sentinel) pair:
>> _M_range_prepend, _M_range_append, _M_range_empl.
>> To make them usable in all standard modes, _M_emplace_aux is defined for 
>> c++98
>> as accepting const value_type&, and _M_insert_aux forwards to it.
>>
>>         PR libstdc++/111055
>>
>> libstdc++-v3/ChangeLog:
>>
>>         * include/bits/deque.tcc (deque::insert_range, 
>> __detail::__advance_dist):
>>         Define.
>>         (deque::_M_range_prepend, deque::_M_range_append):
>>         Extract from _M_range_insert_aux for _ForwardIterator(s).
>>         (deque::_M_range_empl): Define.
>>         (deque::_M_emplace_aux(iterator, const value_type&)): Renamed 
>> _M_insert_aux
>>         in c++98.
>>         * include/bits/stl_deque.h (deque::prepend_range, 
>> deque::append_range),
>>         (deque::assing_range):Define.
>>         deque(from_range_t, _Rg&&, const allocator_type&): Define constructor
>>         and deduction guide.
>>         * include/debug/deque (prepend_range, append_range, assing_range):
>>         Define.
>>         deque(from_range_t, _Rg&&, const allocator_type&): Define constructor
>>         and deduction guide.
>>         (deque::_M_insert_qux): Define using _M_emplace_aux also for c++98.
>>         (deque::_M_range_insert(iterator, _InputIterator, _InputIterator,
>>         std::input_iterator_tag)): Forward to _M_range_empl.
>>         * testsuite/23_containers/deque/cons/from_range.cc: New test.
>>         * testsuite/23_containers/deque/modifiers/append_range.cc: New test.
>>         * testsuite/23_containers/deque/modifiers/assign/assign_range.cc: 
>> New test.
>>         * testsuite/23_containers/deque/modifiers/prepend_range.cc: New test.
>> ---
>>  Testing on x86_64-linux. Tests in 23_containers passed with each of:
>>  -std=c++98, GLIBCXX_DEBUG and no-PCH.
>>  OK for trunk?
>>
>>  In the __advance_dist I branch between __it += __n and 
>> ranges::advance(__it, ranges::end(__rg)),
>>  instead of just calling ranges::advance(__it, __n), as the former will 
>> handle nearly
>>  common ranges, like `int const*` and `int*`.
>>
>>  libstdc++-v3/include/bits/deque.tcc           | 170 ++++++++++++++----
>>  libstdc++-v3/include/bits/stl_deque.h         | 148 ++++++++++++++-
>>  libstdc++-v3/include/debug/deque              |  52 ++++++
>>  .../23_containers/deque/cons/from_range.cc    |  99 ++++++++++
>>  .../deque/modifiers/append_range.cc           |  88 +++++++++
>>  .../deque/modifiers/assign/assign_range.cc    | 109 +++++++++++
>>  .../deque/modifiers/insert/insert_range.cc    | 116 ++++++++++++
>>  .../deque/modifiers/prepend_range.cc          |  90 ++++++++++
>>  8 files changed, 829 insertions(+), 43 deletions(-)
>>  create mode 100644 
>> libstdc++-v3/testsuite/23_containers/deque/cons/from_range.cc
>>  create mode 100644 
>> libstdc++-v3/testsuite/23_containers/deque/modifiers/append_range.cc
>>  create mode 100644 
>> libstdc++-v3/testsuite/23_containers/deque/modifiers/assign/assign_range.cc
>>  create mode 100644 
>> libstdc++-v3/testsuite/23_containers/deque/modifiers/insert/insert_range.cc
>>  create mode 100644 
>> libstdc++-v3/testsuite/23_containers/deque/modifiers/prepend_range.cc
>>
>> diff --git a/libstdc++-v3/include/bits/deque.tcc 
>> b/libstdc++-v3/include/bits/deque.tcc
>> index fcbecca55b4..8444d810e03 100644
>> --- a/libstdc++-v3/include/bits/deque.tcc
>> +++ b/libstdc++-v3/include/bits/deque.tcc
>> @@ -584,13 +584,72 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>>      }
>>
>>    template <typename _Tp, typename _Alloc>
>> -    template <typename _InputIterator>
>> +    template <typename _InputIterator, typename _Sentinel>
>>        void
>>        deque<_Tp, _Alloc>::
>> -      _M_range_insert_aux(iterator __pos,
>> -                         _InputIterator __first, _InputIterator __last,
>> -                         std::input_iterator_tag)
>> -      { std::copy(__first, __last, std::inserter(*this, __pos)); }
>> +      _M_range_prepend(_InputIterator __first, _Sentinel __last,
>> +                     size_type __n)
>> +      {
>> +        iterator __new_start = _M_reserve_elements_at_front(__n);
>> +        __try
>> +          {
>> +            std::__uninitialized_copy_a(_GLIBCXX_MOVE(__first), __last,
>> +                                        __new_start, _M_get_Tp_allocator());
>> +            this->_M_impl._M_start = __new_start;
>> +          }
>> +        __catch(...)
>> +          {
>> +            _M_destroy_nodes(__new_start._M_node,
>> +                             this->_M_impl._M_start._M_node);
>> +            __throw_exception_again;
>> +          }
>> +      }
>> +
>> +  template <typename _Tp, typename _Alloc>
>> +    template <typename _InputIterator, typename _Sentinel>
>> +      void
>> +      deque<_Tp, _Alloc>::
>> +      _M_range_append(_InputIterator __first, _Sentinel __last,
>> +                     size_type __n)
>> +     {
>> +       iterator __new_finish = _M_reserve_elements_at_back(__n);
>> +       __try
>> +       {
>> +         std::__uninitialized_copy_a(_GLIBCXX_MOVE(__first), __last,
>> +                                     this->_M_impl._M_finish,
>> +                                     _M_get_Tp_allocator());
>> +         this->_M_impl._M_finish = __new_finish;
>> +       }
>> +      __catch(...)
>> +       {
>> +         _M_destroy_nodes(this->_M_impl._M_finish._M_node + 1,
>> +                          __new_finish._M_node + 1);
>> +         __throw_exception_again;
>> +       }
>> +     }
>> +
>> + template <typename _Tp, typename _Alloc>
>> +    template <typename _InputIterator, typename _Sentinel>
>> +      void
>> +      deque<_Tp, _Alloc>::
>> +      _M_range_empl(iterator __pos, _InputIterator __first, _Sentinel 
>> __last)
>> +      {
>> +       if (__pos._M_cur != this->_M_impl._M_finish._M_cur)
>> +         for (; __first != __last; (void)++__first, ++__pos)
>> +           __pos = _M_emplace_aux(__pos, *__first);
>> +       else
>> +         for (; __first != __last; ++__first)
>> +           if (this->_M_impl._M_finish._M_cur
>> +               != this->_M_impl._M_finish._M_last - 1)
>> +             {
>> +               _Alloc_traits::construct(this->_M_impl,
>> +                                        this->_M_impl._M_finish._M_cur,
>> +                                        *__first);
>> +               ++this->_M_impl._M_finish._M_cur;
>> +             }
>> +          else
>> +             _M_push_back_aux(*__first);
>> +      }
>>
>>    template <typename _Tp, typename _Alloc>
>>      template <typename _ForwardIterator>
>> @@ -605,38 +664,9 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>>           return;
>>
>>         if (__pos._M_cur == this->_M_impl._M_start._M_cur)
>> -         {
>> -           iterator __new_start = _M_reserve_elements_at_front(__n);
>> -           __try
>> -             {
>> -               std::__uninitialized_copy_a(__first, __last, __new_start,
>> -                                           _M_get_Tp_allocator());
>> -               this->_M_impl._M_start = __new_start;
>> -             }
>> -           __catch(...)
>> -             {
>> -               _M_destroy_nodes(__new_start._M_node,
>> -                                this->_M_impl._M_start._M_node);
>> -               __throw_exception_again;
>> -             }
>> -         }
>> +         _M_range_prepend(__first, __last, __n);
>>         else if (__pos._M_cur == this->_M_impl._M_finish._M_cur)
>> -         {
>> -           iterator __new_finish = _M_reserve_elements_at_back(__n);
>> -           __try
>> -             {
>> -               std::__uninitialized_copy_a(__first, __last,
>> -                                           this->_M_impl._M_finish,
>> -                                           _M_get_Tp_allocator());
>> -               this->_M_impl._M_finish = __new_finish;
>> -             }
>> -           __catch(...)
>> -             {
>> -               _M_destroy_nodes(this->_M_impl._M_finish._M_node + 1,
>> -                                __new_finish._M_node + 1);
>> -               __throw_exception_again;
>> -             }
>> -         }
>> +         _M_range_append(__first, __last, __n);
>>         else
>>           _M_insert_aux(__pos, __first, __last, __n);
>>        }
>> @@ -652,7 +682,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>>  #else
>>      typename deque<_Tp, _Alloc>::iterator
>>        deque<_Tp, _Alloc>::
>> -      _M_insert_aux(iterator __pos, const value_type& __x)
>> +      _M_emplace_aux(iterator __pos, const value_type& __x)
>>        {
>>         value_type __x_copy = __x; // XXX copy
>>  #endif
>> @@ -857,6 +887,72 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>>         }
>>        }
>>
>> +#if __glibcxx_ranges_to_container // C++ >= 23
>> +namespace __detail
>
> I get compilation errors about missing __detail::__container_compatible_range 
> being not defined
> when compiling in GLIBCXX_DEBUG mode, I plan to remove the namespace.

In debug mode this is std::_GLIBCXX_STD_C::__detail and so it hides
std::__detail where the __container_compatible_range concept is
defined.

You should be able to fix it by using
std::_detail::__container_compatible_range later in this file, or you
could rename this __detail namespace to __deque, or just remove it.


>>
>> +{
>> +  template<ranges::forward_range _Rg>
>> +    auto __advance_dist(_Rg& __rg)
>> +    {
>> +      struct _Res
>> +       {
>> +         ranges::iterator_t<_Rg> __last;
>> +         ranges::range_difference_t<_Rg> __size;
>> +       };
>> +      if constexpr (ranges::common_range<_Rg>)
>> +       return _Res{ranges::end(__rg), ranges::distance(__rg)};
>> +      else if constexpr (ranges::sized_range<_Rg>)
>> +       {
>> +         auto const __n = ranges::distance(__rg);
>> +         auto __it = ranges::begin(__rg);
>> +         if constexpr (ranges::random_access_range<_Rg>)
>> +           __it += __n;
>> +         else
>> +           ranges::advance(__it, ranges::end(__rg));
>> +         return _Res{__it, __n};
>> +       }
>> +      else
>> +       {
>> +         auto __it = ranges::begin(__rg);
>> +         auto const __last = ranges::end(__rg);
>> +         ranges::range_difference_t<_Rg> __n(0);
>> +         for (; __it != __last; ++__it)
>> +           ++__n;
>> +         return _Res{__it, __n};
>> +       }
>> +    }
>> +}
>> +
>> +  template<typename _Tp, typename _Alloc>
>> +    template<__detail::__container_compatible_range<_Tp> _Rg>
>> +      constexpr auto
>> +      deque<_Tp, _Alloc>::
>> +      insert_range(const_iterator __pos, _Rg&& __rg)
>> +      -> iterator
>> +      {
>> +       if (__pos == begin())
>> +         {
>> +           prepend_range(std::forward<_Rg>(__rg));
>> +           return begin();
>> +         }
>> +
>> +       const auto __ins_idx = __pos - begin();
>> +       if (__pos == cend())
>> +         append_range(std::forward<_Rg>(__rg));
>> +       else if constexpr (ranges::forward_range<_Rg>)
>> +         {
>> +           auto [__last, __n] = __detail::__advance_dist(__rg);
>> +           if (__builtin_expect(__n != 0, 1))
>> +             _M_insert_aux(__pos._M_const_cast(),
>> +                           ranges::begin(__rg), __last,
>> +                           __n);
>> +         }
>> +       else
>> +         // TODO Optimize sized input ranges: space could be reserved.
>> +         _M_range_empl(__pos._M_const_cast(), ranges::begin(__rg), 
>> ranges::end(__rg));
>> +       return begin() + __ins_idx;
>> +      }
>> +#endif // ranges_to_container
>> +
>>     template<typename _Tp, typename _Alloc>
>>       void
>>       deque<_Tp, _Alloc>::
>> diff --git a/libstdc++-v3/include/bits/stl_deque.h 
>> b/libstdc++-v3/include/bits/stl_deque.h
>> index 69367140c8e..d9153788f29 100644
>> --- a/libstdc++-v3/include/bits/stl_deque.h
>> +++ b/libstdc++-v3/include/bits/stl_deque.h
>> @@ -66,6 +66,10 @@
>>  #if __cplusplus > 201703L
>>  # 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>
>>
>> @@ -1019,6 +1023,18 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>>         }
>>  #endif
>>
>> +#if __glibcxx_ranges_to_container // C++ >= 23
>> +      /**
>> +       * @brief Construct a deque from a range.
>> +       * @since C++23
>> +       */
>> +      template<__detail::__container_compatible_range<_Tp> _Rg>
>> +       constexpr
>> +       deque(from_range_t, _Rg&& __rg, const allocator_type& __a = _Alloc())
>> +       : deque(__a)
>> +       { append_range(std::forward<_Rg>(__rg)); }
>> +#endif
>> +
>>        /**
>>         *  The dtor only erases the elements, and note that if the elements
>>         *  themselves are pointers, the pointed-to memory is not touched in 
>> any
>> @@ -1135,6 +1151,51 @@ _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 deque.
>> +       * @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, begin());
>> +                 return _M_erase_at_end(__res.out);
>> +               }
>> +
>> +             auto __rest = ranges::copy_n(ranges::begin(__rg), size(),
>> +                                          begin()).in;
>> +             _M_range_append(std::move(__rest), ranges::end(__rg),
>> +                             __n - size());
>> +           }
>> +         else
>> +           {
>> +             auto __first = ranges::begin(__rg);
>> +             const auto __last = ranges::end(__rg);
>> +             for (iterator  __it = begin(), __end = end();
>> +                  __it != __end; (void)++__first, ++__it)
>> +               {
>> +                 if (__first == __last)
>> +                   return _M_erase_at_end(__it);
>> +
>> +                 *__it = *__first;
>> +               }
>> +
>> +             for (; __first != __last; ++__first)
>> +               emplace_back(*__first);
>> +           }
>> +       }
>> +#endif // ranges_to_container
>> +
>> +
>>        /// Get a copy of the memory allocation object.
>>        _GLIBCXX_NODISCARD
>>        allocator_type
>> @@ -1762,6 +1823,58 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>>         }
>>  #endif
>>
>> +#if __glibcxx_ranges_to_container // C++ >= 23
>> +      /**
>> +       * @brief Insert a range into the deque.
>> +       * @since C++23
>> +       */
>> +      template<__detail::__container_compatible_range<_Tp> _Rg>
>> +       constexpr iterator
>> +       insert_range(const_iterator __pos, _Rg&& __rg);
>> +
>> +      /**
>> +       * @brief Prepend a range at the begining of the deque.
>> +       * @since C++23
>> +       */
>> +      template<__detail::__container_compatible_range<_Tp> _Rg>
>> +       constexpr void
>> +       prepend_range(_Rg&& __rg)
>> +       {
>> +         if constexpr (ranges::forward_range<_Rg> || 
>> ranges::sized_range<_Rg>)
>> +           {
>> +             const size_type __n(ranges::distance(__rg));
>> +             if (__builtin_expect(__n != 0, 1))
>> +               _M_range_prepend(ranges::begin(__rg), ranges::end(__rg), 
>> __n);
>> +           }
>> +         else
>> +           _M_range_empl(begin(), ranges::begin(__rg), ranges::end(__rg));
>> +       }
>> +
>> +      /**
>> +       * @brief Append a range at the end of the deque.
>> +       * @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 size_type __n(ranges::distance(__rg));
>> +             if (__builtin_expect(__n != 0, 1))
>> +               _M_range_append(ranges::begin(__rg), ranges::end(__rg), __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.
>> @@ -2036,11 +2149,27 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>>         }
>>  #endif
>>
>> +      // insert [__first, __last) at the front, assumes distance(__first, 
>> __last) is n
>> +      template<typename _InputIterator, typename _Sentinel>
>> +      void _M_range_prepend(_InputIterator __first, _Sentinel __last,
>> +                           size_type __n);
>> +
>> +      // insert [__first, __last) at the back, assumes distance(__first, 
>> __last) is n
>> +      template<typename _InputIterator, typename _Sentinel>
>> +      void _M_range_append(_InputIterator __first, _Sentinel __last,
>> +                          size_type __n);
>> +
>> +      // insert [__first, __last) at the _pos, be inserting each element 
>> separately
>> +      template<typename _InputIterator, typename _Sentinel>
>> +      void _M_range_empl(iterator __pos,
>> +                        _InputIterator __first, _Sentinel __last);
>> +
>>        // called by the second insert_dispatch above
>>        template<typename _InputIterator>
>>         void
>>         _M_range_insert_aux(iterator __pos, _InputIterator __first,
>> -                           _InputIterator __last, std::input_iterator_tag);
>> +                           _InputIterator __last, std::input_iterator_tag)
>> +       { _M_range_empl(__pos, __first, __last); }
>>
>>        // called by the second insert_dispatch above
>>        template<typename _ForwardIterator>
>> @@ -2057,17 +2186,17 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>>        // called by insert(p,x)
>>  #if __cplusplus < 201103L
>>        iterator
>> -      _M_insert_aux(iterator __pos, const value_type& __x);
>> +      _M_emplace_aux(iterator __pos, const value_type& __x);
>>  #else
>> -      iterator
>> -      _M_insert_aux(iterator __pos, const value_type& __x)
>> -      { return _M_emplace_aux(__pos, __x); }
>> -
>>        template<typename... _Args>
>>         iterator
>>         _M_emplace_aux(iterator __pos, _Args&&... __args);
>>  #endif
>>
>> +      iterator
>> +      _M_insert_aux(iterator __pos, const value_type& __x)
>> +      { return _M_emplace_aux(__pos, __x); }
>> +
>>        // called by insert(p,n,x) via fill_insert
>>        void
>>        _M_insert_aux(iterator __pos, size_type __n, const value_type& __x);
>> @@ -2281,6 +2410,13 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>>            typename = _RequireAllocator<_Allocator>>
>>      deque(_InputIterator, _InputIterator, _Allocator = _Allocator())
>>        -> deque<_ValT, _Allocator>;
>> +
>> +#if __glibcxx_ranges_to_container // C++ >= 23
>> +  template<ranges::input_range _Rg,
>> +          typename _Alloc = allocator<ranges::range_value_t<_Rg>>>
>> +    deque(from_range_t, _Rg&&, _Alloc = _Alloc())
>> +      -> deque<ranges::range_value_t<_Rg>, _Alloc>;
>> +#endif
>>  #endif
>>
>>    /**
>> diff --git a/libstdc++-v3/include/debug/deque 
>> b/libstdc++-v3/include/debug/deque
>> index 0422acf3496..eb66f8a9590 100644
>> --- a/libstdc++-v3/include/debug/deque
>> +++ b/libstdc++-v3/include/debug/deque
>> @@ -155,6 +155,14 @@ namespace __debug
>>                 __gnu_debug::__base(__last), __a)
>>         { }
>>
>> +#if __glibcxx_ranges_to_container // C++ >= 23
>> +      template<__detail::__container_compatible_range<_Tp> _Rg>
>> +       constexpr
>> +       deque(from_range_t, _Rg&& __rg, const _Allocator& __a = _Allocator())
>> +       : _Base(from_range, std::forward<_Rg>(__rg), __a)
>> +       { }
>> +#endif
>> +
>>        deque(_Base_ref __x)
>>        : _Base(__x._M_ref) { }
>>
>> @@ -210,6 +218,16 @@ namespace __debug
>>        }
>>  #endif
>>
>> +#if __glibcxx_ranges_to_container // C++ >= 23
>> +      template<std::__detail::__container_compatible_range<_Tp> _Rg>
>> +       constexpr void
>> +       assign_range(_Rg&& __rg)
>> +       {
>> +         _Base::assign_range(std::forward<_Rg>(__rg));
>> +         this->_M_invalidate_all();
>> +       }
>> +#endif
>> +
>>        using _Base::get_allocator;
>>
>>        // iterators:
>> @@ -544,6 +562,33 @@ namespace __debug
>>         }
>>  #endif
>>
>> +#if __glibcxx_ranges_to_container // C++ >= 23
>> +      template<__detail::__container_compatible_range<_Tp> _Rg>
>> +       constexpr iterator
>> +       insert_range(const_iterator __pos, _Rg&& __rg)
>> +       {
>> +         auto __res = _Base::insert(__pos.base(), std::forward<_Rg>(__rg));
>> +         this->_M_invalidate_all();
>> +         return iterator(__res, this);
>> +       }
>> +
>> +      template<std::__detail::__container_compatible_range<_Tp> _Rg>
>> +       constexpr void
>> +       prepend_range(_Rg&& __rg)
>> +       {
>> +         _Base::prepend_range(std::forward<_Rg>(__rg));
>> +         this->_M_invalidate_all();
>> +       }
>> +
>> +      template<std::__detail::__container_compatible_range<_Tp> _Rg>
>> +       constexpr void
>> +       append_range(_Rg&& __rg)
>> +       {
>> +         _Base::append_range(std::forward<_Rg>(__rg));
>> +         this->_M_invalidate_all();
>> +       }
>> +#endif
>> +
>>        void
>>        pop_front() _GLIBCXX_NOEXCEPT
>>        {
>> @@ -667,6 +712,13 @@ namespace __debug
>>            typename = _RequireAllocator<_Allocator>>
>>      deque(size_t, _Tp, _Allocator = _Allocator())
>>        -> deque<_Tp, _Allocator>;
>> +
>> +#if __glibcxx_ranges_to_container // C++ >= 23
>> +  template<ranges::input_range _Rg,
>> +          typename _Alloc = allocator<ranges::range_value_t<_Rg>>>
>> +    deque(from_range_t, _Rg&&, _Alloc = _Alloc())
>> +      -> deque<ranges::range_value_t<_Rg>, _Alloc>;
>> +#endif
>>  #endif
>>
>>    template<typename _Tp, typename _Alloc>
>> diff --git a/libstdc++-v3/testsuite/23_containers/deque/cons/from_range.cc 
>> b/libstdc++-v3/testsuite/23_containers/deque/cons/from_range.cc
>> new file mode 100644
>> index 00000000000..73e7e724065
>> --- /dev/null
>> +++ b/libstdc++-v3/testsuite/23_containers/deque/cons/from_range.cc
>> @@ -0,0 +1,99 @@
>> +// { dg-do run { target c++23 } }
>> +
>> +#include <deque>
>> +#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::deque d(std::from_range, r);
>> +  static_assert(std::is_same_v<decltype(d), std::deque<long>>);
>> +
>> +  using Alloc = __gnu_test::SimpleAllocator<long>;
>> +  Alloc alloc;
>> +  std::deque d2(std::from_range, r, alloc);
>> +  static_assert(std::is_same_v<decltype(d2), std::deque<long, Alloc>>);
>> +}
>> +
>> +template<typename Range, typename Alloc>
>> +constexpr void
>> +do_test(Alloc alloc)
>> +{
>> +  // The deque'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::deque<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::deque<V, Alloc> d0(std::from_range, Range(a, a+0));
>> +  VERIFY( d0.empty() );
>> +  VERIFY( d0.get_allocator() == Alloc() );
>> +
>> +  std::deque<V, Alloc> d4(std::from_range, Range(a, a+4));
>> +  VERIFY( eq(d4, {a, 4}) );
>> +  VERIFY( d4.get_allocator() == Alloc() );
>> +
>> +  std::deque<V, Alloc> d9(std::from_range, Range(a, a+9), alloc);
>> +  VERIFY( eq(d9, {a, 9}) );
>> +  VERIFY( d9.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_nocopy<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 int
>> +  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;
>> +}
>> +
>> +int main()
>> +{
>> +  test_ranges();
>> +}
>> diff --git 
>> a/libstdc++-v3/testsuite/23_containers/deque/modifiers/append_range.cc 
>> b/libstdc++-v3/testsuite/23_containers/deque/modifiers/append_range.cc
>> new file mode 100644
>> index 00000000000..a4eebcbc96d
>> --- /dev/null
>> +++ b/libstdc++-v3/testsuite/23_containers/deque/modifiers/append_range.cc
>> @@ -0,0 +1,88 @@
>> +// { dg-do run { target c++23 } }
>> +
>> +#include <deque>
>> +#include <span>
>> +#include <testsuite_hooks.h>
>> +#include <testsuite_iterators.h>
>> +#include <testsuite_allocator.h>
>> +
>> +template<typename Range, typename Alloc>
>> +constexpr void
>> +do_test()
>> +{
>> +  // The deque'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::deque<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::deque<V, Alloc> d;
>> +  d.append_range(r4);
>> +  VERIFY( eq(d, {a, 4}) );
>> +  d.append_range(r5);
>> +  VERIFY( eq(d, a) );
>> +  d.append_range(Range(a, a));
>> +  VERIFY( eq(d, a) );
>> +  d.clear();
>> +  d.append_range(Range(a, a));
>> +  VERIFY( d.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_nocopy<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 int
>> +  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;
>> +}
>> +
>> +int main()
>> +{
>> +  test_ranges();
>> +}
>> diff --git 
>> a/libstdc++-v3/testsuite/23_containers/deque/modifiers/assign/assign_range.cc
>>  
>> b/libstdc++-v3/testsuite/23_containers/deque/modifiers/assign/assign_range.cc
>> new file mode 100644
>> index 00000000000..0cfdd3b2c0f
>> --- /dev/null
>> +++ 
>> b/libstdc++-v3/testsuite/23_containers/deque/modifiers/assign/assign_range.cc
>> @@ -0,0 +1,109 @@
>> +// { dg-do run { target c++23 } }
>> +
>> +#include <deque>
>> +#include <span>
>> +#include <testsuite_hooks.h>
>> +#include <testsuite_iterators.h>
>> +#include <testsuite_allocator.h>
>> +
>> +template<typename Range, typename Alloc>
>> +constexpr void
>> +do_test()
>> +{
>> +  // The deque'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::deque<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;
>> +  };
>> +
>> +  // assign to empty deque
>> +  std::deque<V, Alloc> d;
>> +  d.assign_range(Range(a, a));
>> +  VERIFY( d.empty() );
>> +  d.assign_range(Range(a, a+4));
>> +  VERIFY( eq(d, {a, 4}) );
>> +  d.clear();
>> +  d.assign_range(Range(a, a+9));
>> +  VERIFY( eq(d, a) );
>> +  d.clear();
>> +  d.assign_range(Range(a, a+4));
>> +  VERIFY( eq(d, {a, 4}) );
>> +  d.clear();
>> +  d.assign_range(Range(a, a+9));
>> +  VERIFY( eq(d, a) );
>> +
>> +
>> +  // assign to non-empty deque
>> +  d.assign_range(Range(a, a+4)); // smaller than size()
>> +  VERIFY( eq(d, {a, 4}) );
>> +  d.assign_range(Range(a, a+9)); // larger than size()
>> +  VERIFY( eq(d, a) );
>> +  d.resize(1);
>> +  d.assign_range(Range(a, a+4)); // larger than size()
>> +  VERIFY( eq(d, {a, 4}) );
>> +  d.clear();
>> +  d.resize(4);
>> +  d.assign_range(Range(a, a+4)); // equal to size()
>> +  VERIFY( eq(d, {a, 4}) );
>> +  d.shrink_to_fit();
>> +  d.assign_range(Range(a, a+9));
>> +  VERIFY( eq(d, a) );
>> +  d.assign_range(Range(a, a));
>> +  VERIFY( d.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_nocopy<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 int
>> +  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;
>> +}
>> +
>> +int main()
>> +{
>> +  test_ranges();
>> +}
>> diff --git 
>> a/libstdc++-v3/testsuite/23_containers/deque/modifiers/insert/insert_range.cc
>>  
>> b/libstdc++-v3/testsuite/23_containers/deque/modifiers/insert/insert_range.cc
>> new file mode 100644
>> index 00000000000..c5a8b3fc7c3
>> --- /dev/null
>> +++ 
>> b/libstdc++-v3/testsuite/23_containers/deque/modifiers/insert/insert_range.cc
>> @@ -0,0 +1,116 @@
>> +// { dg-do run { target c++23 } }
>> +
>> +#include <deque>
>> +#include <span>
>> +#include <testsuite_hooks.h>
>> +#include <testsuite_iterators.h>
>> +#include <testsuite_allocator.h>
>> +
>> +template<typename Range, typename Alloc>
>> +constexpr void
>> +do_test()
>> +{
>> +  // The deque'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::deque<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::deque<V, Alloc> d;
>> +  d.insert_range(d.begin(), Range(a, a));
>> +  VERIFY( d.empty() );
>> +  d.insert_range(d.begin(), Range(a, a+4));
>> +  VERIFY( eq(d, {a, a+4}) );
>> +  d.clear();
>> +  d.insert_range(d.begin(), Range(a+4, a+9));
>> +  VERIFY( eq(d, {a+4, a+9}) );
>> +  d.insert_range(d.begin(), Range(a, a+4));
>> +  VERIFY( eq(d, a) );
>> +  d.clear();
>> +  d.shrink_to_fit();
>> +
>> +  d.insert_range(d.end(), Range(a, a));
>> +  VERIFY( d.empty() );
>> +  d.insert_range(d.end(), Range(a, a+4));
>> +  VERIFY( eq(d, {a, a+4}) );
>> +  d.clear();
>> +  d.insert_range(d.end(), Range(a, a+4));
>> +  VERIFY( eq(d, {a, a+4}) );
>> +  d.insert_range(d.end(), Range(a+4, a+9));
>> +  VERIFY( eq(d, a) );
>> +  d.clear();
>> +  d.shrink_to_fit();
>> +
>> +  auto it = d.insert_range(d.begin(), Range(a, a+3));
>> +  VERIFY( it == d.begin() );
>> +  it = d.insert_range(d.end(), Range(a+6, a+9));
>> +  VERIFY( it == d.begin()+3 );
>> +  it = d.insert_range(d.begin()+3, Range(a+3, a+6));
>> +  VERIFY( it == d.begin()+3 );
>> +  VERIFY( eq(d, a) );
>> +  d.resize(3);
>> +  it = d.insert_range(d.begin()+1, Range(a+4, a+9));
>> +  VERIFY( it == d.begin()+1 );
>> +  it = d.insert_range(d.begin()+1, Range(a+1, a+4));
>> +  VERIFY( it == d.begin()+1 );
>> +  d.resize(9);
>> +  VERIFY( eq(d, a) );
>> +  it = d.insert_range(d.begin() + 6, Range(a, a));
>> +  VERIFY( it == d.begin() + 6 );
>> +  VERIFY( eq(d, 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_nocopy<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 int
>> +  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;
>> +}
>> +int main()
>> +{
>> +  test_ranges();
>> +}
>> diff --git 
>> a/libstdc++-v3/testsuite/23_containers/deque/modifiers/prepend_range.cc 
>> b/libstdc++-v3/testsuite/23_containers/deque/modifiers/prepend_range.cc
>> new file mode 100644
>> index 00000000000..7cb57df7826
>> --- /dev/null
>> +++ b/libstdc++-v3/testsuite/23_containers/deque/modifiers/prepend_range.cc
>> @@ -0,0 +1,90 @@
>> +// { dg-do run { target c++23 } }
>> +
>> +#include <deque>
>> +#include <span>
>> +#include <testsuite_hooks.h>
>> +#include <testsuite_iterators.h>
>> +#include <testsuite_allocator.h>
>> +
>> +template<typename Range, typename Alloc>
>> +constexpr void
>> +do_test()
>> +{
>> +  // The deque'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::deque<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+5, a+9);
>> +  Range r5(a+0, a+5);
>> +
>> +  std::deque<V, Alloc> d;
>> +  d.prepend_range(r4);
>> +  VERIFY( d.size() == 4 );
>> +  VERIFY( d[0] == 6 );
>> +  VERIFY( eq(d, {a+5, 4}) );
>> +  d.prepend_range(r5);
>> +  VERIFY( eq(d, a) );
>> +  d.prepend_range(Range(a, a));
>> +  VERIFY( eq(d, a) );
>> +  d.clear();
>> +  d.prepend_range(Range(a, a));
>> +  VERIFY( d.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_nocopy<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 int
>> +  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;
>> +}
>> +
>> +int main()
>> +{
>> +  test_ranges();
>> +}
>> --
>> 2.48.1
>>

Reply via email to