On Mon, 29 Apr 2024, Jonathan Wakely wrote:

> On Mon, 22 Apr 2024 at 22:43, Patrick Palka wrote:
> >
> > Tested on x86_64-pc-linux-gnu, does this look OK for trunk?  More tests
> > are needed but I figured I'd submit this now for possible consideration into
> > GCC 14 since we're getting close to release..  All changes are confined to
> > C++26.
> 
> OK for trunk. Maybe we can backport it for 14.2 later, but not now.
> Sorry for the review being slow.

No worries, thanks a lot!  I pushed this now, though I realized I didn't
implement the latest/approved revision of the paper, R8 vs R7, which
notably changes the constraints on operator-(it, default_sentinel).
Since that seems to be the only significant change, I reckon I'll fix
that in a follow-up patch.

> 
> 
> >
> > -- >8 --
> >
> > libstdc++-v3/ChangeLog:
> >
> >         * include/bits/version.def (ranges_concat): Define.
> >         * include/bits/version.h: Regenerate.
> >         * include/std/ranges (__detail::__concat_reference_t): Define
> >         for C++26.
> >         (__detail::__concat_value_t): Likewise.
> >         (__detail::__concat_rvalue_reference_t): Likewise.
> >         (__detail::__concat_indirectly_readable_impl): Likewise.
> >         (__detail::__concat_indirectly_readable): Likewise.
> >         (__detail::__concatable): Likewise.
> >         (__detail::__all_but_last_common): Likewise.
> >         (__detail::__concat_is_random_access): Likewise.
> >         (__detail::__concat_is_bidirectional): Likewise.
> >         (__detail::__last_is_common): Likewise.
> >         (concat_view): Likewise.
> >         (__detail::__concat_view_iter_cat): Likewise.
> >         (concat_view::iterator): Likewise.
> >         (views::__detail::__can_concat_view): Likewise.
> >         (views::_Concat, views::concat): Likewise.
> >         * testsuite/std/ranges/concat/1.cc: New test.
> > ---
> >  libstdc++-v3/include/bits/version.def         |   8 +
> >  libstdc++-v3/include/bits/version.h           |  10 +
> >  libstdc++-v3/include/std/ranges               | 584 ++++++++++++++++++
> >  libstdc++-v3/testsuite/std/ranges/concat/1.cc |  61 ++
> >  4 files changed, 663 insertions(+)
> >  create mode 100644 libstdc++-v3/testsuite/std/ranges/concat/1.cc
> >
> > diff --git a/libstdc++-v3/include/bits/version.def 
> > b/libstdc++-v3/include/bits/version.def
> > index 5c0477fb61e..af13090c094 100644
> > --- a/libstdc++-v3/include/bits/version.def
> > +++ b/libstdc++-v3/include/bits/version.def
> > @@ -1796,6 +1796,14 @@ ftms = {
> >    };
> >  };
> >
> > +ftms = {
> > +  name = ranges_concat;
> > +  values = {
> > +    v = 202403;
> > +    cxxmin = 26;
> > +  };
> > +};
> > +
> >  // Standard test specifications.
> >  stds[97] = ">= 199711L";
> >  stds[03] = ">= 199711L";
> > diff --git a/libstdc++-v3/include/bits/version.h 
> > b/libstdc++-v3/include/bits/version.h
> > index 65e708c73fb..1f27bfe050d 100644
> > --- a/libstdc++-v3/include/bits/version.h
> > +++ b/libstdc++-v3/include/bits/version.h
> > @@ -2003,4 +2003,14 @@
> >  #endif /* !defined(__cpp_lib_to_string) && 
> > defined(__glibcxx_want_to_string) */
> >  #undef __glibcxx_want_to_string
> >
> > +#if !defined(__cpp_lib_ranges_concat)
> > +# if (__cplusplus >  202302L)
> > +#  define __glibcxx_ranges_concat 202403L
> > +#  if defined(__glibcxx_want_all) || defined(__glibcxx_want_ranges_concat)
> > +#   define __cpp_lib_ranges_concat 202403L
> > +#  endif
> > +# endif
> > +#endif /* !defined(__cpp_lib_ranges_concat) && 
> > defined(__glibcxx_want_ranges_concat) */
> > +#undef __glibcxx_want_ranges_concat
> > +
> >  #undef __glibcxx_want_all
> > diff --git a/libstdc++-v3/include/std/ranges 
> > b/libstdc++-v3/include/std/ranges
> > index afce818376b..28a39bf6f34 100644
> > --- a/libstdc++-v3/include/std/ranges
> > +++ b/libstdc++-v3/include/std/ranges
> > @@ -55,6 +55,7 @@
> >  #define __glibcxx_want_ranges_as_const
> >  #define __glibcxx_want_ranges_as_rvalue
> >  #define __glibcxx_want_ranges_cartesian_product
> > +#define __glibcxx_want_ranges_concat
> >  #define __glibcxx_want_ranges_chunk
> >  #define __glibcxx_want_ranges_chunk_by
> >  #define __glibcxx_want_ranges_enumerate
> > @@ -9514,6 +9515,589 @@ namespace __detail
> >  } // namespace ranges
> >  #endif // __cpp_lib_ranges_to_container
> >
> > +#if __cpp_lib_ranges_concat // C++ >= C++26
> > +namespace ranges
> > +{
> > +  namespace __detail
> > +  {
> > +    template<typename... _Rs>
> > +      using __concat_reference_t = 
> > common_reference_t<range_reference_t<_Rs>...>;
> > +
> > +    template<typename... _Rs>
> > +      using __concat_value_t = common_type_t<range_value_t<_Rs>...>;
> > +
> > +    template<typename... _Rs>
> > +      using __concat_rvalue_reference_t
> > +       = common_reference_t<range_rvalue_reference_t<_Rs>...>;
> > +
> > +    template<typename _Ref, typename _RRef, typename _It>
> > +      concept __concat_indirectly_readable_impl = requires (const _It 
> > __it) {
> > +       { *__it } -> convertible_to<_Ref>;
> > +       { ranges::iter_move(__it) } -> convertible_to<_RRef>;
> > +      };
> > +
> > +    template<typename... _Rs>
> > +      concept __concat_indirectly_readable
> > +       = common_reference_with<__concat_reference_t<_Rs...>&&, 
> > __concat_value_t<_Rs...>&>
> > +         && common_reference_with<__concat_reference_t<_Rs...>&&,
> > +                                  __concat_rvalue_reference_t<_Rs...>&&>
> > +         && common_reference_with<__concat_rvalue_reference_t<_Rs...>&&,
> > +                                  __concat_value_t<_Rs...> const&>
> > +         && 
> > (__concat_indirectly_readable_impl<__concat_reference_t<_Rs...>,
> > +                                               
> > __concat_rvalue_reference_t<_Rs...>,
> > +                                               iterator_t<_Rs>>
> > +             && ...);
> > +
> > +    template<typename... _Rs>
> > +      concept __concatable = requires {
> > +       typename __concat_reference_t<_Rs...>;
> > +       typename __concat_value_t<_Rs...>;
> > +       typename __concat_rvalue_reference_t<_Rs...>;
> > +      } && __concat_indirectly_readable<_Rs...>;
> > +
> > +    template<bool _Const, typename _Range, typename... _Rs>
> > +      struct __all_but_last_common
> > +      {
> > +       static inline constexpr bool value
> > +         = requires { requires (common_range<__maybe_const_t<_Const, 
> > _Range>>
> > +                                && __all_but_last_common<_Const, 
> > _Rs...>::value); };
> > +      };
> > +
> > +    template<bool _Const, typename _Range>
> > +      struct __all_but_last_common<_Const, _Range>
> > +      { static inline constexpr bool value = true; };
> > +
> > +    template<bool _Const, typename... _Rs>
> > +      concept __concat_is_random_access = __all_random_access<_Const, 
> > _Rs...>
> > +       && __all_but_last_common<_Const, _Rs...>::value;
> > +
> > +    template<bool _Const, typename... _Rs>
> > +      concept __concat_is_bidirectional = __all_bidirectional<_Const, 
> > _Rs...>
> > +       && __all_but_last_common<_Const, _Rs...>::value;
> > +
> > +    template<typename _Range, typename... _Rs>
> > +      struct __last_is_common
> > +      { static inline constexpr bool value = 
> > __last_is_common<_Rs...>::value; };
> > +
> > +    template<typename _Range>
> > +      struct __last_is_common<_Range>
> > +      { static inline constexpr bool value = common_range<_Range>; };
> > +  } // namespace __detail
> > +
> > +  template<input_range... _Vs>
> > +    requires (view<_Vs> && ...) && (sizeof...(_Vs) > 0) && 
> > __detail::__concatable<_Vs...>
> > +  class concat_view : public view_interface<concat_view<_Vs...>>
> > +  {
> > +    tuple<_Vs...> _M_views;
> > +
> > +    template<bool _Const> class iterator;
> > +
> > +  public:
> > +    constexpr concat_view() = default;
> > +
> > +    constexpr explicit
> > +    concat_view(_Vs... __views)
> > +    : _M_views(std::move(__views)...)
> > +    { }
> > +
> > +    constexpr iterator<false>
> > +    begin() requires(!(__detail::__simple_view<_Vs> && ...))
> > +    {
> > +      iterator<false> __it(this, in_place_index<0>, 
> > ranges::begin(std::get<0>(_M_views)));
> > +      __it.template _M_satisfy<0>();
> > +      return __it;
> > +    }
> > +
> > +    constexpr iterator<true>
> > +    begin() const requires (range<const _Vs> && ...) && 
> > __detail::__concatable<const _Vs...>
> > +    {
> > +      iterator<true> __it(this, in_place_index<0>, 
> > ranges::begin(std::get<0>(_M_views)));
> > +      __it.template _M_satisfy<0>();
> > +      return __it;
> > +    }
> > +
> > +    constexpr auto
> > +    end() requires(!(__detail::__simple_view<_Vs> && ...))
> > +    {
> > +      if constexpr (__detail::__last_is_common<_Vs...>::value)
> > +       {
> > +         constexpr auto __n = sizeof...(_Vs);
> > +         return iterator<false>(this, in_place_index<__n - 1>,
> > +                                ranges::end(std::get<__n - 1>(_M_views)));
> > +       }
> > +      else
> > +       return default_sentinel;
> > +    }
> > +
> > +    constexpr auto
> > +    end() const requires (range<const _Vs> && ...) && 
> > __detail::__concatable<const _Vs...>
> > +    {
> > +      if constexpr (__detail::__last_is_common<const _Vs...>::value)
> > +       {
> > +         constexpr auto __n = sizeof...(_Vs);
> > +         return iterator<true>(this, in_place_index<__n - 1>,
> > +                               ranges::end(std::get<__n - 1>(_M_views)));
> > +       }
> > +      else
> > +       return default_sentinel;
> > +    }
> > +
> > +    constexpr auto
> > +    size() requires (sized_range<_Vs>&&...)
> > +    {
> > +      return std::apply([](auto... __sizes) {
> > +       using _CT = 
> > __detail::__make_unsigned_like_t<common_type_t<decltype(__sizes)...>>;
> > +       return (_CT(__sizes) + ...);
> > +      }, __detail::__tuple_transform(ranges::size, _M_views));
> > +    }
> > +
> > +    constexpr auto
> > +    size() const requires (sized_range<const _Vs>&&...)
> > +    {
> > +      return std::apply([](auto... __sizes) {
> > +       using _CT = 
> > __detail::__make_unsigned_like_t<common_type_t<decltype(__sizes)...>>;
> > +       return (_CT(__sizes) + ...);
> > +      }, __detail::__tuple_transform(ranges::size, _M_views));
> > +    }
> > +  };
> > +
> > +  template <typename... _Rs>
> > +    concat_view(_Rs&&...) -> concat_view<views::all_t<_Rs>...>;
> > +
> > +  namespace __detail
> > +  {
> > +    template<bool _Const, typename... _Vs>
> > +      struct __concat_view_iter_cat
> > +      { };
> > +
> > +    template<bool _Const, typename... _Vs>
> > +      requires __detail::__all_forward<_Const, _Vs...>
> > +      struct __concat_view_iter_cat<_Const, _Vs...>
> > +      {
> > +       static auto
> > +       _S_iter_cat()
> > +       {
> > +         if constexpr 
> > (!is_reference_v<__concat_reference_t<__maybe_const_t<_Const, _Vs>...>>)
> > +           return input_iterator_tag{};
> > +         else
> > +           return []<typename... _Cats>(_Cats... __cats) {
> > +             if constexpr ((derived_from<_Cats, 
> > random_access_iterator_tag> && ...)
> > +                           && __concat_is_random_access<_Const, _Vs...>)
> > +               return random_access_iterator_tag{};
> > +             else if constexpr ((derived_from<_Cats, 
> > bidirectional_iterator_tag> && ...)
> > +                                && __concat_is_bidirectional<_Const, 
> > _Vs...>)
> > +               return bidirectional_iterator_tag{};
> > +             else if constexpr ((derived_from<_Cats, forward_iterator_tag> 
> > && ...))
> > +               return forward_iterator_tag{};
> > +             else
> > +               return input_iterator_tag{};
> > +           }(typename iterator_traits<iterator_t<__maybe_const_t<_Const, 
> > _Vs>>>
> > +             ::iterator_category{}...);
> > +       }
> > +      };
> > +  }
> > +
> > +  template<input_range... _Vs>
> > +    requires (view<_Vs> && ...) && (sizeof...(_Vs) > 0) && 
> > __detail::__concatable<_Vs...>
> > +  template<bool _Const>
> > +  class concat_view<_Vs...>::iterator
> > +  : public __detail::__concat_view_iter_cat<_Const, _Vs...>
> > +  {
> > +    static auto
> > +    _S_iter_concept()
> > +    {
> > +      if constexpr (__detail::__concat_is_random_access<_Const, _Vs...>)
> > +       return random_access_iterator_tag{};
> > +      else if constexpr (__detail::__concat_is_bidirectional<_Const, 
> > _Vs...>)
> > +       return bidirectional_iterator_tag{};
> > +      else if constexpr (__detail::__all_forward<_Const, _Vs...>)
> > +       return forward_iterator_tag{};
> > +      else
> > +       return input_iterator_tag{};
> > +    }
> > +
> > +    friend concat_view;
> > +    friend iterator<!_Const>;
> > +
> > +  public:
> > +    // iterator_category defined in __concat_view_iter_cat
> > +    using iterator_concept = decltype(_S_iter_concept());
> > +    using value_type = __detail::__concat_value_t<__maybe_const_t<_Const, 
> > _Vs>...>;
> > +    using difference_type = 
> > common_type_t<range_difference_t<__maybe_const_t<_Const, _Vs>>...>;
> > +
> > +  private:
> > +    using __base_iter = variant<iterator_t<__maybe_const_t<_Const, 
> > _Vs>>...>;
> > +
> > +    __maybe_const_t<_Const, concat_view>* _M_parent = nullptr;
> > +    __base_iter _M_it;
> > +
> > +    template<size_t _Nm>
> > +      constexpr void
> > +      _M_satisfy()
> > +      {
> > +       if constexpr (_Nm < (sizeof...(_Vs) - 1))
> > +         {
> > +           if (std::get<_Nm>(_M_it) == 
> > ranges::end(std::get<_Nm>(_M_parent->_M_views)))
> > +             {
> > +               _M_it.template emplace<_Nm + 1>(ranges::begin
> > +                                               (std::get<_Nm + 
> > 1>(_M_parent->_M_views)));
> > +               _M_satisfy<_Nm + 1>();
> > +             }
> > +         }
> > +      }
> > +
> > +    template<size_t _Nm>
> > +      constexpr void
> > +      _M_prev()
> > +      {
> > +       if constexpr (_Nm == 0)
> > +         --std::get<0>(_M_it);
> > +       else
> > +         {
> > +           if (std::get<_Nm>(_M_it) == 
> > ranges::begin(std::get<_Nm>(_M_parent->_M_views)))
> > +             {
> > +               _M_it.template emplace<_Nm - 1>(ranges::end
> > +                                               (std::get<_Nm - 
> > 1>(_M_parent->_M_views)));
> > +               _M_prev<_Nm - 1>();
> > +             }
> > +           else
> > +             --std::get<_Nm>(_M_it);
> > +         }
> > +      }
> > +
> > +    template<size_t _Nm>
> > +      constexpr void
> > +      _M_advance_fwd(difference_type __offset, difference_type __steps)
> > +      {
> > +       using _Dt = iter_difference_t<variant_alternative_t<_Nm, 
> > __base_iter>>;
> > +       if constexpr (_Nm == sizeof...(_Vs) - 1)
> > +         std::get<_Nm>(_M_it) += static_cast<_Dt>(__steps);
> > +       else
> > +         {
> > +           auto __n_size = 
> > ranges::distance(std::get<_Nm>(_M_parent->_M_views));
> > +           if (__offset + __steps < __n_size)
> > +               std::get<_Nm>(_M_it) += static_cast<_Dt>(__steps);
> > +           else
> > +             {
> > +               _M_it.template emplace<_Nm + 1>(ranges::begin
> > +                                               (std::get<_Nm + 
> > 1>(_M_parent->_M_views)));
> > +               _M_advance_fwd<_Nm + 1>(0, __offset + __steps - __n_size);
> > +             }
> > +         }
> > +      }
> > +
> > +    template<size_t _Nm>
> > +      constexpr void
> > +      _M_advance_bwd(difference_type __offset, difference_type __steps)
> > +      {
> > +       using _Dt = iter_difference_t<variant_alternative_t<_Nm, 
> > __base_iter>>;
> > +       if constexpr (_Nm == 0)
> > +         std::get<_Nm>(_M_it) -= static_cast<_Dt>(__steps);
> > +       else {
> > +           if (__offset >= __steps)
> > +             std::get<_Nm>(_M_it) -= static_cast<_Dt>(__steps);
> > +           else
> > +             {
> > +               auto __prev_size = ranges::distance(std::get<_Nm - 
> > 1>(_M_parent->_M_views));
> > +               _M_it.template emplace<_Nm - 1>(ranges::end
> > +                                               (std::get<_Nm - 
> > 1>(_M_parent->_M_views)));
> > +               _M_advance_bwd<_Nm - 1>(__prev_size, __steps - __offset);
> > +             }
> > +       }
> > +      }
> > +
> > +    // Invoke the function object __f, which has a call operator taking a 
> > size_t
> > +    // index template parameter (bounded by the number of underlying 
> > views),
> > +    // according the runtime value of __index.
> > +    template<typename _Fp>
> > +      static constexpr auto
> > +      _S_invoke_with_runtime_index(_Fp&& __f, size_t __index)
> > +      {
> > +       return [&__f, __index]<size_t _Idx>(this auto&& __self) {
> > +         if (_Idx == __index)
> > +           return __f.template operator()<_Idx>();
> > +         if constexpr (_Idx + 1 < sizeof...(_Vs))
> > +           return __self.template operator()<_Idx + 1>();
> > +       }.template operator()<0>();
> > +      }
> > +
> > +    template<typename _Fp>
> > +      constexpr auto
> > +      _M_invoke_with_runtime_index(_Fp&& __f)
> > +      { return _S_invoke_with_runtime_index(std::forward<_Fp>(__f), 
> > _M_it.index()); }
> > +
> > +    template<typename... _Args>
> > +      explicit constexpr
> > +      iterator(__maybe_const_t<_Const, concat_view>* __parent, _Args&&... 
> > __args)
> > +       requires constructible_from<__base_iter, _Args&&...>
> > +      : _M_parent(__parent), _M_it(std::forward<_Args>(__args)...)
> > +      { }
> > +
> > +  public:
> > +    iterator() = default;
> > +
> > +    constexpr
> > +    iterator(iterator<!_Const> __it)
> > +      requires _Const && (convertible_to<iterator_t<_Vs>, iterator_t<const 
> > _Vs>> && ...)
> > +    : _M_parent(__it._M_parent)
> > +    {
> > +      _M_invoke_with_runtime_index([this, &__it]<size_t _Idx>() {
> > +       _M_it.template emplace<_Idx>(std::get<_Idx>(std::move(__it._M_it)));
> > +      });
> > +    }
> > +
> > +    constexpr decltype(auto)
> > +    operator*() const
> > +    {
> > +      __glibcxx_assert(!_M_it.valueless_by_exception());
> > +      using reference = 
> > __detail::__concat_reference_t<__maybe_const_t<_Const, _Vs>...>;
> > +      return std::visit([](auto&& __it) -> reference { return *__it; }, 
> > _M_it);
> > +    }
> > +
> > +    constexpr iterator&
> > +    operator++()
> > +    {
> > +      _M_invoke_with_runtime_index([this]<size_t _Idx>() {
> > +       ++std::get<_Idx>(_M_it);
> > +       _M_satisfy<_Idx>();
> > +      });
> > +      return *this;
> > +    }
> > +
> > +    constexpr void
> > +    operator++(int)
> > +    {
> > +      ++*this;
> > +    }
> > +
> > +    constexpr iterator
> > +    operator++(int)
> > +      requires __detail::__all_forward<_Const, _Vs...>
> > +    {
> > +      auto __tmp = *this;
> > +      ++*this;
> > +      return __tmp;
> > +    }
> > +
> > +    constexpr iterator&
> > +    operator--()
> > +      requires __detail::__concat_is_bidirectional<_Const, _Vs...>
> > +    {
> > +      __glibcxx_assert(!_M_it.valueless_by_exception());
> > +      _M_invoke_with_runtime_index([this]<size_t _Idx>() {
> > +       _M_prev<_Idx>();
> > +      });
> > +      return *this;
> > +    }
> > +
> > +    constexpr iterator
> > +    operator--(int)
> > +      requires __detail::__concat_is_bidirectional<_Const, _Vs...>
> > +    {
> > +      auto __tmp = *this;
> > +      --*this;
> > +      return __tmp;
> > +    }
> > +
> > +    constexpr iterator&
> > +    operator+=(difference_type __n)
> > +      requires __detail::__concat_is_random_access<_Const, _Vs...>
> > +    {
> > +      __glibcxx_assert(!_M_it.valueless_by_exception());
> > +      _M_invoke_with_runtime_index([this, __n]<size_t _Idx>() {
> > +       auto __begin = ranges::begin(std::get<_Idx>(_M_parent->_M_views));
> > +       if (__n > 0)
> > +         _M_advance_fwd<_Idx>(std::get<_Idx>(_M_it) - __begin, __n);
> > +       else if (__n < 0)
> > +         _M_advance_bwd<_Idx>(std::get<_Idx>(_M_it) - __begin, -__n);
> > +      });
> > +      return *this;
> > +    }
> > +
> > +    constexpr iterator&
> > +    operator-=(difference_type __n)
> > +      requires __detail::__concat_is_random_access<_Const, _Vs...>
> > +    {
> > +      *this += -__n;
> > +      return *this;
> > +    }
> > +
> > +    constexpr decltype(auto)
> > +    operator[](difference_type __n) const
> > +      requires __detail::__concat_is_random_access<_Const, _Vs...>
> > +    { return *((*this) + __n); }
> > +
> > +    friend constexpr bool
> > +    operator==(const iterator& __x, const iterator& __y)
> > +      requires (equality_comparable<iterator_t<__maybe_const_t<_Const, 
> > _Vs>>> && ...)
> > +    {
> > +      __glibcxx_assert(!__x._M_it.valueless_by_exception());
> > +      __glibcxx_assert(!__y._M_it.valueless_by_exception());
> > +      return __x._M_it == __y._M_it;
> > +    }
> > +
> > +    friend constexpr bool
> > +    operator==(const iterator& __it, default_sentinel_t)
> > +    {
> > +      __glibcxx_assert(!__it._M_it.valueless_by_exception());
> > +      constexpr auto __last_idx = sizeof...(_Vs) - 1;
> > +      return (__it._M_it.index() == __last_idx
> > +             && (std::get<__last_idx>(__it._M_it)
> > +                 == 
> > ranges::end(std::get<__last_idx>(__it._M_parent->_M_views))));
> > +    }
> > +
> > +    friend constexpr bool
> > +    operator<(const iterator& __x, const iterator& __y)
> > +      requires __detail::__all_random_access<_Const, _Vs...>
> > +    { return __x._M_it < __y._M_it; }
> > +
> > +    friend constexpr bool
> > +    operator>(const iterator& __x, const iterator& __y)
> > +      requires __detail::__all_random_access<_Const, _Vs...>
> > +    { return __x._M_it > __y._M_it; }
> > +
> > +    friend constexpr bool
> > +    operator<=(const iterator& __x, const iterator& __y)
> > +      requires __detail::__all_random_access<_Const, _Vs...>
> > +    { return __x._M_it <= __y._M_it; }
> > +
> > +    friend constexpr bool
> > +    operator>=(const iterator& __x, const iterator& __y)
> > +      requires __detail::__all_random_access<_Const, _Vs...>
> > +    { return __x._M_it >= __y._M_it; }
> > +
> > +    friend constexpr auto
> > +    operator<=>(const iterator& __x, const iterator& __y)
> > +      requires (__detail::__all_random_access<_Const, _Vs...>
> > +               && (three_way_comparable<iterator_t<__maybe_const_t<_Const, 
> > _Vs>>>
> > +                   && ...))
> > +    { return __x._M_it <=> __y._M_it; }
> > +
> > +    friend constexpr iterator
> > +    operator+(const iterator& __it, difference_type __n)
> > +      requires __detail::__concat_is_random_access<_Const, _Vs...>
> > +    { return auto(__it) += __n; }
> > +
> > +    friend constexpr iterator
> > +    operator+(difference_type __n, const iterator& __it)
> > +      requires __detail::__concat_is_random_access<_Const, _Vs...>
> > +    { return __it + __n; }
> > +
> > +    friend constexpr iterator
> > +    operator-(const iterator& __it, difference_type __n)
> > +      requires __detail::__concat_is_random_access<_Const, _Vs...>
> > +    { return auto(__it) -= __n; }
> > +
> > +    friend constexpr difference_type
> > +    operator-(const iterator& __x, const iterator& __y)
> > +      requires __detail::__concat_is_random_access<_Const, _Vs...>
> > +    {
> > +      return _S_invoke_with_runtime_index([&]<size_t _Ix>() -> 
> > difference_type {
> > +        return _S_invoke_with_runtime_index([&]<size_t _Iy>() -> 
> > difference_type {
> > +         if constexpr (_Ix > _Iy)
> > +           {
> > +             auto __dy = ranges::distance(std::get<_Iy>(__y._M_it),
> > +                                          
> > ranges::end(std::get<_Iy>(__y._M_parent
> > +                                                                    
> > ->_M_views)));
> > +             auto __dx = 
> > ranges::distance(ranges::begin(std::get<_Ix>(__x._M_parent
> > +                                                                      
> > ->_M_views)),
> > +                                          std::get<_Ix>(__x._M_it));
> > +             difference_type __sum = 0;
> > +             [&]<size_t _Idx = _Iy + 1>(this auto&& __self) {
> > +               if constexpr (_Idx < _Ix)
> > +                 {
> > +                   __sum += 
> > ranges::size(std::get<_Idx>(__x._M_parent->_M_views));
> > +                   __self.template operator()<_Idx + 1>();
> > +                 }
> > +             }();
> > +             return __dy + __sum + __dx;
> > +           }
> > +         else if constexpr (_Ix < _Iy)
> > +           return -(__y - __x);
> > +         else
> > +           return std::get<_Ix>(__x._M_it) - std::get<_Iy>(__y._M_it);
> > +       }, __y._M_it.index());
> > +      }, __x._M_it.index());
> > +    }
> > +
> > +    friend constexpr difference_type
> > +    operator-(const iterator& __x, default_sentinel_t)
> > +      requires __detail::__concat_is_random_access<_Const, _Vs...>
> > +       && __detail::__last_is_common<__maybe_const_t<_Const, 
> > _Vs>...>::value
> > +    {
> > +      return _S_invoke_with_runtime_index([&]<size_t _Ix>() -> 
> > difference_type {
> > +       auto __dx = ranges::distance(std::get<_Ix>(__x._M_it),
> > +                                    
> > ranges::end(std::get<_Ix>(__x._M_parent->_M_views)));
> > +       difference_type __sum = 0;
> > +       [&]<size_t _Idx = _Ix + 1>(this auto&& __self) {
> > +         if constexpr (_Idx < sizeof...(_Vs))
> > +           {
> > +             __sum += 
> > ranges::size(std::get<_Idx>(__x._M_parent->_M_views));
> > +             __self.template operator()<_Idx + 1>();
> > +           }
> > +       }();
> > +       return -(__dx + __sum);
> > +      }, __x._M_it.index());
> > +    }
> > +
> > +    friend constexpr difference_type
> > +    operator-(default_sentinel_t, const iterator& __x)
> > +      requires __detail::__concat_is_random_access<_Const, _Vs...>
> > +       && __detail::__last_is_common<__maybe_const_t<_Const, 
> > _Vs>...>::value
> > +    { return -(__x - default_sentinel); }
> > +
> > +    friend constexpr decltype(auto)
> > +    iter_move(const iterator& __it)
> > +    {
> > +      using _Res = 
> > __detail::__concat_rvalue_reference_t<__maybe_const_t<_Const, _Vs>...>;
> > +      return std::visit([](const auto& __i) -> _Res {
> > +       return ranges::iter_move(__i);
> > +      }, __it._M_it);
> > +    }
> > +
> > +    friend constexpr void
> > +    iter_swap(const iterator& __x, const iterator& __y)
> > +      requires swappable_with<iter_reference_t<iterator>,
> > +                             iter_reference_t<iterator>>
> > +       && (... && indirectly_swappable<iterator_t<__maybe_const_t<_Const, 
> > _Vs>>>)
> > +    {
> > +      std::visit([&](const auto& __it1, const auto& __it2) {
> > +       if constexpr (is_same_v<decltype(__it1), decltype(__it2)>)
> > +         ranges::iter_swap(__it1, __it2);
> > +       else
> > +         ranges::swap(*__it1, *__it2);
> > +      }, __x._M_it, __y._M_it);
> > +    }
> > +  };
> > +
> > +  namespace views
> > +  {
> > +    namespace __detail
> > +    {
> > +      template<typename... _Ts>
> > +       concept __can_concat_view = requires { 
> > concat_view(std::declval<_Ts>()...); };
> > +    }
> > +
> > +    struct _Concat
> > +    {
> > +      template<typename... _Ts>
> > +       requires __detail::__can_concat_view<_Ts...>
> > +      constexpr auto
> > +      operator() [[nodiscard]] (_Ts&&... __ts) const
> > +      {
> > +       if constexpr (sizeof...(_Ts) == 1)
> > +         return views::all(std::forward<_Ts>(__ts)...);
> > +       else
> > +         return concat_view(std::forward<_Ts>(__ts)...);
> > +      }
> > +    };
> > +
> > +    inline constexpr _Concat concat;
> > +  }
> > +
> > +} // namespace ranges
> > +#endif // __cpp_lib_ranges_concat
> > +
> >  _GLIBCXX_END_NAMESPACE_VERSION
> >  } // namespace std
> >  #endif // library concepts
> > diff --git a/libstdc++-v3/testsuite/std/ranges/concat/1.cc 
> > b/libstdc++-v3/testsuite/std/ranges/concat/1.cc
> > new file mode 100644
> > index 00000000000..91c8893cdc5
> > --- /dev/null
> > +++ b/libstdc++-v3/testsuite/std/ranges/concat/1.cc
> > @@ -0,0 +1,61 @@
> > +// { dg-do run { target c++26 } }
> > +// { dg-add-options no_pch }
> > +
> > +#include <ranges>
> > +
> > +#if __cpp_lib_ranges_concat != 202403L
> > +# error "Feature-test macro __cpp_lib_ranges_concat has wrong value in 
> > <ranges>"
> > +#endif
> > +
> > +#include <algorithm>
> > +#include <vector>
> > +#include <array>
> > +#include <utility>
> > +#include <testsuite_hooks.h>
> > +#include <testsuite_iterators.h>
> > +
> > +namespace ranges = std::ranges;
> > +namespace views = std::views;
> > +
> > +constexpr bool
> > +test01()
> > +{
> > +  std::vector<int> v1{1, 2, 3}, v2{4, 5}, v3{};
> > +  std::array a{6, 7, 8};
> > +  auto s = views::single(9);
> > +
> > +  auto v = views::concat(v1, v2, v3, a, s);
> > +  VERIFY( ranges::size(v) == 9 );
> > +  VERIFY( ranges::equal(v, views::iota(1, 10)) );
> > +  VERIFY( ranges::equal(v, views::iota(1, 10)) );
> > +  VERIFY( ranges::equal(v | views::reverse,
> > +                       views::iota(1, 10) | views::reverse) );
> > +
> > +  auto it0 = v.begin();
> > +  auto cit = std::as_const(v).begin();
> > +  VERIFY( it0 == it0 );
> > +  VERIFY( cit == cit );
> > +  VERIFY( it0 == cit );
> > +  VERIFY( it0 - it0 == 0);
> > +  for (int i = 0; i < 10; i++)
> > +    {
> > +      VERIFY( it0 + i - it0 == i);
> > +      VERIFY( it0 + i - i + i == it0 + i );
> > +      VERIFY( it0 + i - (it0 + i) == 0 );
> > +    }
> > +  VERIFY( std::default_sentinel - it0 == 9 );
> > +  VERIFY( it0 + 9 == std::default_sentinel );
> > +
> > +  auto it5 = it0+5;
> > +  ranges::iter_swap(it0, it5);
> > +  VERIFY( *it0 == 6 && *it5 == 1 );
> > +  ranges::iter_swap(it0, it5);
> > +  *it0 = ranges::iter_move(it0);
> > +  return true;
> > +}
> > +
> > +int
> > +main()
> > +{
> > +  static_assert(test01());
> > +}
> > --
> > 2.45.0.rc0
> >
> 
> 

Reply via email to