diff mbox series

[committed,v2] libstdc++: Define std::ranges::to for C++23 (P1206R7) [PR111055]

Message ID 20231123175247.2451163-1-jwakely@redhat.com
State New
Headers show
Series [committed,v2] libstdc++: Define std::ranges::to for C++23 (P1206R7) [PR111055] | expand

Commit Message

Jonathan Wakely Nov. 23, 2023, 5:51 p.m. UTC
Here's the finished version of the std::ranges::to patch, which I've
pushed to trunk.

Tested x86_64-linux.

-- >8 --

This adds the std::ranges::to functions for C++23. The rest of P1206R7
is not yet implemented, i.e. the new constructors taking the
std::from_range tag, and the new insert_range, assign_range, etc. member
functions. std::ranges::to works with the standard containers even
without the new constructors, so this is useful immediately.

The __cpp_lib_ranges_to_container feature test macro can be defined now,
because that only indicates support for the changes in <ranges>, which
are implemented by this patch. The __cpp_lib_containers_ranges macro
will be defined once all containers support the new member functions.

libstdc++-v3/ChangeLog:

	PR libstdc++/111055
	* include/bits/ranges_base.h (from_range_t): Define new tag
	type.
	(from_range): Define new tag object.
	* include/bits/version.def (ranges_to_container): Define.
	* include/bits/version.h: Regenerate.
	* include/std/ranges (ranges::to): Define.
	* testsuite/std/ranges/conv/1.cc: New test.
	* testsuite/std/ranges/conv/2_neg.cc: New test.
	* testsuite/std/ranges/conv/version.cc: New test.
---
 libstdc++-v3/include/bits/ranges_base.h       |   8 +-
 libstdc++-v3/include/bits/version.def         |  34 +-
 libstdc++-v3/include/bits/version.h           | 111 +++---
 libstdc++-v3/include/std/ranges               | 361 ++++++++++++++++-
 libstdc++-v3/testsuite/std/ranges/conv/1.cc   | 369 ++++++++++++++++++
 .../testsuite/std/ranges/conv/2_neg.cc        |  24 ++
 .../testsuite/std/ranges/conv/version.cc      |  19 +
 7 files changed, 866 insertions(+), 60 deletions(-)
 create mode 100644 libstdc++-v3/testsuite/std/ranges/conv/1.cc
 create mode 100644 libstdc++-v3/testsuite/std/ranges/conv/2_neg.cc
 create mode 100644 libstdc++-v3/testsuite/std/ranges/conv/version.cc

Comments

Hans-Peter Nilsson Nov. 28, 2023, 12:02 a.m. UTC | #1
> From: Jonathan Wakely <jwakely@redhat.com>
> Date: Thu, 23 Nov 2023 17:51:38 +0000

> libstdc++-v3/ChangeLog:
> 
> 	PR libstdc++/111055
> 	* include/bits/ranges_base.h (from_range_t): Define new tag
> 	type.
> 	(from_range): Define new tag object.
> 	* include/bits/version.def (ranges_to_container): Define.
> 	* include/bits/version.h: Regenerate.
> 	* include/std/ranges (ranges::to): Define.
> 	* testsuite/std/ranges/conv/1.cc: New test.
> 	* testsuite/std/ranges/conv/2_neg.cc: New test.
> 	* testsuite/std/ranges/conv/version.cc: New test.

JFTR, for the list: this (r14-5794-g7a6a29c455e775) caused
another one of those wonderful "xtreme test" regressions.

Logged as PR112737: "[14 Regression]
g++.dg/modules/xtreme-header-2_b.C -std=c++2b (test for
excess errors)", and pinskia quickly linked it to the
meta-bug for modules issues, PR103524 (thanks!)

IIRC, sometimes those tests show bugs elsewhere, suggesting
lack of coverage in other tests (and not just streaming
aspects), but this one actually mentions modules in key
error messages.

brgds, H-P
Patrick Palka Nov. 29, 2023, 4:28 p.m. UTC | #2
On Thu, 23 Nov 2023, Jonathan Wakely wrote:

> Here's the finished version of the std::ranges::to patch, which I've
> pushed to trunk.
> 
> Tested x86_64-linux.
> 
> -- >8 --
> 
> This adds the std::ranges::to functions for C++23. The rest of P1206R7
> is not yet implemented, i.e. the new constructors taking the
> std::from_range tag, and the new insert_range, assign_range, etc. member
> functions. std::ranges::to works with the standard containers even
> without the new constructors, so this is useful immediately.
> 
> The __cpp_lib_ranges_to_container feature test macro can be defined now,
> because that only indicates support for the changes in <ranges>, which
> are implemented by this patch. The __cpp_lib_containers_ranges macro
> will be defined once all containers support the new member functions.
> 
> libstdc++-v3/ChangeLog:
> 
> 	PR libstdc++/111055
> 	* include/bits/ranges_base.h (from_range_t): Define new tag
> 	type.
> 	(from_range): Define new tag object.
> 	* include/bits/version.def (ranges_to_container): Define.
> 	* include/bits/version.h: Regenerate.
> 	* include/std/ranges (ranges::to): Define.
> 	* testsuite/std/ranges/conv/1.cc: New test.
> 	* testsuite/std/ranges/conv/2_neg.cc: New test.
> 	* testsuite/std/ranges/conv/version.cc: New test.
> ---
>  libstdc++-v3/include/bits/ranges_base.h       |   8 +-
>  libstdc++-v3/include/bits/version.def         |  34 +-
>  libstdc++-v3/include/bits/version.h           | 111 +++---
>  libstdc++-v3/include/std/ranges               | 361 ++++++++++++++++-
>  libstdc++-v3/testsuite/std/ranges/conv/1.cc   | 369 ++++++++++++++++++
>  .../testsuite/std/ranges/conv/2_neg.cc        |  24 ++
>  .../testsuite/std/ranges/conv/version.cc      |  19 +
>  7 files changed, 866 insertions(+), 60 deletions(-)
>  create mode 100644 libstdc++-v3/testsuite/std/ranges/conv/1.cc
>  create mode 100644 libstdc++-v3/testsuite/std/ranges/conv/2_neg.cc
>  create mode 100644 libstdc++-v3/testsuite/std/ranges/conv/version.cc
> 
> diff --git a/libstdc++-v3/include/bits/ranges_base.h b/libstdc++-v3/include/bits/ranges_base.h
> index 7fa43d1965a..1ca2c5ce2bb 100644
> --- a/libstdc++-v3/include/bits/ranges_base.h
> +++ b/libstdc++-v3/include/bits/ranges_base.h
> @@ -37,6 +37,7 @@
>  #include <bits/stl_iterator.h>
>  #include <ext/numeric_traits.h>
>  #include <bits/max_size_type.h>
> +#include <bits/version.h>
>  
>  #ifdef __cpp_lib_concepts
>  namespace std _GLIBCXX_VISIBILITY(default)
> @@ -1056,8 +1057,13 @@ namespace ranges
>      using borrowed_iterator_t = __conditional_t<borrowed_range<_Range>,
>  						iterator_t<_Range>,
>  						dangling>;
> -
>  } // 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{};
> +#endif
> +
>  _GLIBCXX_END_NAMESPACE_VERSION
>  } // namespace std
>  #endif // library concepts
> diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def
> index 605708dfee7..140777832ed 100644
> --- a/libstdc++-v3/include/bits/version.def
> +++ b/libstdc++-v3/include/bits/version.def
> @@ -1439,19 +1439,21 @@ ftms = {
>    };
>  };
>  
> -ftms = {
> -  name = to_underlying;
> -  values = {
> -    v = 202102;
> -    cxxmin = 23;
> -  };
> -};
> +//ftms = {
> +//  name = container_ranges;
> +//  values = {
> +//    v = 202202;
> +//    cxxmin = 23;
> +//    hosted = yes;
> +//  };
> +//};
>  
>  ftms = {
> -  name = unreachable;
> +  name = ranges_to_container;
>    values = {
>      v = 202202;
>      cxxmin = 23;
> +    hosted = yes;
>    };
>  };
>  
> @@ -1683,6 +1685,22 @@ ftms = {
>    };
>  };
>  
> +ftms = {
> +  name = to_underlying;
> +  values = {
> +    v = 202102;
> +    cxxmin = 23;
> +  };
> +};
> +
> +ftms = {
> +  name = unreachable;
> +  values = {
> +    v = 202202;
> +    cxxmin = 23;
> +  };
> +};
> +
>  ftms = {
>    name = fstream_native_handle;
>    values = {
> diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h
> index cacd9375cab..1fb1d148459 100644
> --- a/libstdc++-v3/include/bits/version.h
> +++ b/libstdc++-v3/include/bits/version.h
> @@ -1740,29 +1740,18 @@
>  #endif /* !defined(__cpp_lib_reference_from_temporary) && defined(__glibcxx_want_reference_from_temporary) */
>  #undef __glibcxx_want_reference_from_temporary
>  
> -// from version.def line 1443
> -#if !defined(__cpp_lib_to_underlying)
> -# if (__cplusplus >= 202100L)
> -#  define __glibcxx_to_underlying 202102L
> -#  if defined(__glibcxx_want_all) || defined(__glibcxx_want_to_underlying)
> -#   define __cpp_lib_to_underlying 202102L
> +// from version.def line 1452
> +#if !defined(__cpp_lib_ranges_to_container)
> +# if (__cplusplus >= 202100L) && _GLIBCXX_HOSTED
> +#  define __glibcxx_ranges_to_container 202202L
> +#  if defined(__glibcxx_want_all) || defined(__glibcxx_want_ranges_to_container)
> +#   define __cpp_lib_ranges_to_container 202202L
>  #  endif
>  # endif
> -#endif /* !defined(__cpp_lib_to_underlying) && defined(__glibcxx_want_to_underlying) */
> -#undef __glibcxx_want_to_underlying
> +#endif /* !defined(__cpp_lib_ranges_to_container) && defined(__glibcxx_want_ranges_to_container) */
> +#undef __glibcxx_want_ranges_to_container
>  
> -// from version.def line 1451
> -#if !defined(__cpp_lib_unreachable)
> -# if (__cplusplus >= 202100L)
> -#  define __glibcxx_unreachable 202202L
> -#  if defined(__glibcxx_want_all) || defined(__glibcxx_want_unreachable)
> -#   define __cpp_lib_unreachable 202202L
> -#  endif
> -# endif
> -#endif /* !defined(__cpp_lib_unreachable) && defined(__glibcxx_want_unreachable) */
> -#undef __glibcxx_want_unreachable
> -
> -// from version.def line 1459
> +// from version.def line 1461
>  #if !defined(__cpp_lib_ranges_zip)
>  # if (__cplusplus >= 202100L)
>  #  define __glibcxx_ranges_zip 202110L
> @@ -2059,7 +2048,29 @@
>  #endif /* !defined(__cpp_lib_string_resize_and_overwrite) && defined(__glibcxx_want_string_resize_and_overwrite) */
>  #undef __glibcxx_want_string_resize_and_overwrite
>  
> -// from version.def line 1687
> +// from version.def line 1689
> +#if !defined(__cpp_lib_to_underlying)
> +# if (__cplusplus >= 202100L)
> +#  define __glibcxx_to_underlying 202102L
> +#  if defined(__glibcxx_want_all) || defined(__glibcxx_want_to_underlying)
> +#   define __cpp_lib_to_underlying 202102L
> +#  endif
> +# endif
> +#endif /* !defined(__cpp_lib_to_underlying) && defined(__glibcxx_want_to_underlying) */
> +#undef __glibcxx_want_to_underlying
> +
> +// from version.def line 1697
> +#if !defined(__cpp_lib_unreachable)
> +# if (__cplusplus >= 202100L)
> +#  define __glibcxx_unreachable 202202L
> +#  if defined(__glibcxx_want_all) || defined(__glibcxx_want_unreachable)
> +#   define __cpp_lib_unreachable 202202L
> +#  endif
> +# endif
> +#endif /* !defined(__cpp_lib_unreachable) && defined(__glibcxx_want_unreachable) */
> +#undef __glibcxx_want_unreachable
> +
> +// from version.def line 1705
>  #if !defined(__cpp_lib_fstream_native_handle)
>  # if (__cplusplus >  202302L) && _GLIBCXX_HOSTED
>  #  define __glibcxx_fstream_native_handle 202306L
> diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges
> index 26d6c013ad0..63bea862c05 100644
> --- a/libstdc++-v3/include/std/ranges
> +++ b/libstdc++-v3/include/std/ranges
> @@ -64,6 +64,7 @@
>  #define __glibcxx_want_ranges_repeat
>  #define __glibcxx_want_ranges_slide
>  #define __glibcxx_want_ranges_stride
> +#define __glibcxx_want_ranges_to_container
>  #define __glibcxx_want_ranges_zip
>  #include <bits/version.h>
>  
> @@ -9213,8 +9214,366 @@ namespace views::__adaptor
>  
>    namespace views = ranges::views;
>  
> +#if __cpp_lib_ranges_to_container // C++ >= 23
> +namespace ranges
> +{
> +/// @cond undocumented
> +namespace __detail
> +{
> +  template<typename _Container>
> +    constexpr bool __reservable_container
> +      = sized_range<_Container>
> +      && requires(_Container& __c, range_size_t<_Container> __n) {
> +	__c.reserve(__n);
> +	{ __c.capacity() } -> same_as<decltype(__n)>;
> +	{ __c.max_size() } -> same_as<decltype(__n)>;
> +      };
> +
> +  template<typename _Container, typename _Ref>
> +    constexpr bool __container_insertable
> +      = requires(_Container& __c, _Ref&& __ref) {
> +	typename _Container::value_type;
> +	requires (
> +	  requires { __c.push_back(std::forward<_Ref>(__ref)); }
> +	  || requires { __c.insert(__c.end(), std::forward<_Ref>(__ref)); }
> +	);
> +      };
> +
> +  template<typename _Ref, typename _Container>
> +    constexpr auto
> +    __container_inserter(_Container& __c)
> +    {
> +      if constexpr (requires { __c.push_back(std::declval<_Ref>()); })
> +	return std::back_inserter(__c);
> +      else
> +	return std::inserter(__c, __c.end());
> +    }
> +
> +  template<typename _Cont, typename _Range>
> +    constexpr bool __toable = requires {
> +      requires (!input_range<_Range>

It seems this sholud be input_range<_Cont>?

> +		  || convertible_to<range_reference_t<_Range>,
> +				    range_value_t<_Cont>>);
> +    };
> +} // namespace __detail
> +/// @endcond
> +
> +  /// Convert a range to a container.
> +  /**
> +   * @tparam _Cont A container type.
> +   * @param __r A range that models the `input_range` concept.
> +   * @param __args... Arguments to pass to the container constructor.
> +   * @since C++23
> +   *
> +   * This function converts a range to the `_Cont` type.
> +   *
> +   * For example, `std::ranges::to<std::vector<int>>(some_view)`
> +   * will convert the view to `std::vector<int>`.
> +   *
> +   * Additional constructor arguments for the container can be supplied after
> +   * the input range argument, e.g.
> +   * `std::ranges::to<std::vector<int, Alloc<int>>>(a_range, an_allocator)`.
> +   */
> +  template<typename _Cont, input_range _Rg, typename... _Args>
> +    requires (!view<_Cont>)
> +    constexpr _Cont
> +    to [[nodiscard]] (_Rg&& __r, _Args&&... __args)
> +    {
> +      static_assert(!is_const_v<_Cont> && !is_volatile_v<_Cont>);
> +      static_assert(is_class_v<_Cont>);
> +
> +      if constexpr (__detail::__toable<_Cont, _Rg>)
> +	{
> +	  if constexpr (constructible_from<_Cont, _Rg, _Args...>)
> +	    return _Cont(std::forward<_Rg>(__r),
> +			 std::forward<_Args>(__args)...);
> +	  else if constexpr (constructible_from<_Cont, from_range_t, _Rg, _Args...>)
> +	    return _Cont(from_range, std::forward<_Rg>(__r),
> +			 std::forward<_Args>(__args)...);
> +	  else if constexpr (requires { common_range<_Rg>;

Missing 'requires' before common_range?

> +		typename __iter_category_t<iterator_t<_Rg>>;
> +		requires derived_from<__iter_category_t<iterator_t<_Rg>>,
> +				      input_iterator_tag>;
> +		requires constructible_from<_Cont, iterator_t<_Rg>,
> +					    sentinel_t<_Rg>, _Args...>;
> +	      })
> +	    return _Cont(ranges::begin(__r), ranges::end(__r),
> +			 std::forward<_Args>(__args)...);
> +	  else
> +	    {
> +	      using __detail::__container_insertable;
> +	      using __detail::__reservable_container;
> +	      using _RefT = range_reference_t<_Rg>;
> +	      static_assert(constructible_from<_Cont, _Args...>);
> +	      static_assert(__container_insertable<_Cont, _RefT>);
> +	      _Cont __c(std::forward<_Args>(__args)...);
> +	      if constexpr (sized_range<_Rg> && __reservable_container<_Cont>)
> +		__c.reserve(static_cast<range_size_t<_Cont>>(ranges::size(__r)));
> +	      auto __ins = __detail::__container_inserter<_RefT>(__c);
> +	      for (auto&& __e : __r)
> +		*__ins++ = std::forward<decltype(__e)>(__e);
> +	      return __c;
> +	    }
> +	}
> +      else
> +	{
> +	  static_assert(input_range<range_reference_t<_Rg>>);
> +	  // _GLIBCXX_RESOLVE_LIB_DEFECTS
> +	  // 3984. ranges::to's recursion branch may be ill-formed
> +	  return ranges::to<_Cont>(ref_view(__r) | views::transform(
> +		[]<typename _Elt>(_Elt&& __elem) {
> +		  using _ValT = range_value_t<_Cont>;
> +		  return ranges::to<_ValT>(std::forward<_Elt>(__elem));
> +		}), std::forward<_Args>(__args)...);
> +	}
> +    }
> +
> +/// @cond undocumented
> +namespace __detail
> +{
> +  template<typename _Rg>
> +    struct _InputIter
> +    {
> +      using iterator_category = input_iterator_tag;
> +      using value_type = range_value_t<_Rg>;
> +      using difference_type = ptrdiff_t;
> +      using pointer = add_pointer_t<range_reference_t<_Rg>>;
> +      using reference = range_reference_t<_Rg>;
> +      reference operator*() const;
> +      pointer operator->() const;
> +      _InputIter& operator++();
> +      _InputIter operator++(int);
> +      bool operator==(const _InputIter&) const;
> +    };
> +
> +#if 0
> +  template<template<typename...> typename _Cont, typename _Rg,
> +	   typename... _Args>
> +    concept __deduce_expr_1 = requires {
> +      _Cont(std::declval<_Rg>(), std::declval<_Args>()...);
> +    };
> +
> +  template<template<typename...> typename _Cont, typename _Rg,
> +	   typename... _Args>
> +    concept __deduce_expr_2 = requires {
> +      _Cont(from_range, std::declval<_Rg>(), std::declval<_Args>()...);
> +    };
> +
> +  template<template<typename...> typename _Cont, typename _Rg,
> +	   typename... _Args>
> +    concept __deduce_expr_3 = requires(_InputIter<_Rg> __i) {
> +      _Cont(std::move(__i), std::move(__i), std::declval<_Args>()...);
> +    };
> +#endif
> +
> +  template<template<typename...> typename _Cont, input_range _Rg,
> +	   typename... _Args>
> +    using _DeduceExpr1
> +      = decltype(_Cont(std::declval<_Rg>(), std::declval<_Args>()...));
> +
> +  template<template<typename...> typename _Cont, input_range _Rg,
> +	   typename... _Args>
> +    using _DeduceExpr2
> +      = decltype(_Cont(from_range, std::declval<_Rg>(),
> +		       std::declval<_Args>()...));
> +
> +  template<template<typename...> typename _Cont, input_range _Rg,
> +	   typename... _Args>
> +    using _DeduceExpr3
> +      = decltype(_Cont(std::declval<_InputIter<_Rg>>(),
> +		       std::declval<_InputIter<_Rg>>(),
> +		       std::declval<_Args>()...));
> +
> +} // namespace __detail
> +/// @endcond
> +
> +  template<template<typename...> typename _Cont, input_range _Rg,
> +	   typename... _Args>
> +    constexpr auto
> +    to [[nodiscard]] (_Rg&& __r, _Args&&... __args)
> +    {
> +      using __detail::_DeduceExpr1;
> +      using __detail::_DeduceExpr2;
> +      using __detail::_DeduceExpr3;
> +      if constexpr (requires { typename _DeduceExpr1<_Cont, _Rg, _Args...>; })
> +	return ranges::to<_DeduceExpr1<_Cont, _Rg, _Args...>>(
> +	    std::forward<_Rg>(__r), std::forward<_Args>(__args)...);
> +      else if constexpr (requires { typename _DeduceExpr2<_Cont, _Rg, _Args...>; })
> +	return ranges::to<_DeduceExpr2<_Cont, _Rg, _Args...>>(
> +	    std::forward<_Rg>(__r), std::forward<_Args>(__args)...);
> +      else if constexpr (requires { typename _DeduceExpr3<_Cont, _Rg, _Args...>; })
> +	return ranges::to<_DeduceExpr3<_Cont, _Rg, _Args...>>(
> +	    std::forward<_Rg>(__r), std::forward<_Args>(__args)...);
> +      else
> +	static_assert(false); // Cannot deduce container specialization.
> +    }
> +
> +/// @cond undocumented
> +namespace __detail
> +{
> +  template<typename _Cont, typename... _Args>
> +    class _ToClosure
> +    : public views::__adaptor::_RangeAdaptorClosure<_ToClosure<_Cont, _Args...>>
> +    {
> +      tuple<decay_t<_Args>...> _M_bound_args;
> +
> +    public:
> +      _ToClosure(_Args&&... __args)

Missing constexpr?

> +      : _M_bound_args(std::forward<_Args>(__args)...)
> +      { }
> +
> +      // TODO: use explicit object functions ("deducing this").
> +
> +      template<typename _Rg>
> +	constexpr auto
> +	operator()(_Rg&& __r) &
> +	{
> +	  return std::apply([&__r]<typename... _Tp>(_Tp&&... __args) {
> +	    return ranges::to<_Cont>(std::forward<_Rg>(__r),
> +				     std::forward<_Tp>(__args)...);
> +	  }, _M_bound_args);
> +	}
> +
> +      template<typename _Rg>
> +	constexpr auto
> +	operator()(_Rg&& __r) const &
> +	{
> +	  return std::apply([&__r]<typename... _Tp>(_Tp&&... __args) {
> +	    return ranges::to<_Cont>(std::forward<_Rg>(__r),
> +				     std::forward<_Tp>(__args)...);
> +	  }, _M_bound_args);
> +	}
> +
> +      template<typename _Rg>
> +	constexpr auto
> +	operator()(_Rg&& __r) &&
> +	{
> +	  return std::apply([&__r]<typename... _Tp>(_Tp&&... __args) {
> +	    return ranges::to<_Cont>(std::forward<_Rg>(__r),
> +				     std::forward<_Tp>(__args)...);
> +	  }, std::move(_M_bound_args));
> +	}
> +
> +      template<typename _Rg>
> +	constexpr auto
> +	operator()(_Rg&& __r) const &&
> +	{
> +	  return std::apply([&__r]<typename... _Tp>(_Tp&&... __args) {
> +	    return ranges::to<_Cont>(std::forward<_Rg>(__r),
> +				     std::forward<_Tp>(__args)...);
> +	  }, std::move(_M_bound_args));
> +	}
> +    };
> +} // namespace __detail
> +/// @endcond
> +
> +  /// ranges::to adaptor for converting a range to a container type
> +  /**
> +   * @tparam _Cont A container type.
> +   * @param __args... Arguments to pass to the container constructor.
> +   * @since C++23
> +   *
> +   * This range adaptor returns a range adaptor closure object that converts
> +   * a range to the `_Cont` type.
> +   *
> +   * For example, `some_view | std::ranges::to<std::vector<int>>()`
> +   * will convert the view to `std::vector<int>`.
> +   *
> +   * Additional constructor arguments for the container can be supplied, e.g.
> +   * `r | std::ranges::to<std::vector<int, Alloc<int>>>(an_allocator)`.
> +   */
> +  template<typename _Cont, typename... _Args>
> +    requires (!view<_Cont>)
> +    constexpr __detail::_ToClosure<_Cont, _Args...>
> +    to [[nodiscard]] (_Args&&... __args)
> +    { return {std::forward<_Args>(__args)...}; }
> +
> +/// @cond undocumented
> +namespace __detail
> +{
> +  template<template<typename...> typename _Cont, typename... _Args>
> +    class _ToClosure2
> +    : public views::__adaptor::_RangeAdaptorClosure<_ToClosure2<_Cont, _Args...>>
> +    {
> +      tuple<decay_t<_Args>...> _M_bound_args;
> +
> +    public:
> +      _ToClosure2(_Args&&... __args)

Same here.

Would it be possible to use _RangeAdaptor instead of _RangeAdaptorClosure to
leverage the existing partial application/forwarding code?  IIUC we'd need to
allow omitting the _S_arity member to mean accept any number of arguments.
I can work on that if anything.

> +      : _M_bound_args(std::forward<_Args>(__args)...)
> +      { }
> +
> +      // TODO: use explicit object functions ("deducing this").
> +
> +      template<typename _Rg>
> +	constexpr auto
> +	operator()(_Rg&& __r) &
> +	{
> +	  return std::apply([&__r]<typename... _Tp>(_Tp&&... __args) {
> +	    return ranges::to<_Cont>(std::forward<_Rg>(__r),
> +				     std::forward<_Tp>(__args)...);
> +	  }, _M_bound_args);
> +	}
> +
> +      template<typename _Rg>
> +	constexpr auto
> +	operator()(_Rg&& __r) const &
> +	{
> +	  return std::apply([&__r]<typename... _Tp>(_Tp&&... __args) {
> +	    return ranges::to<_Cont>(std::forward<_Rg>(__r),
> +				     std::forward<_Tp>(__args)...);
> +	  }, _M_bound_args);
> +	}
> +
> +      template<typename _Rg>
> +	constexpr auto
> +	operator()(_Rg&& __r) &&
> +	{
> +	  return std::apply([&__r]<typename... _Tp>(_Tp&&... __args) {
> +	    return ranges::to<_Cont>(std::forward<_Rg>(__r),
> +				     std::forward<_Tp>(__args)...);
> +	  }, std::move(_M_bound_args));
> +	}
> +
> +      template<typename _Rg>
> +	constexpr auto
> +	operator()(_Rg&& __r) const &&
> +	{
> +	  return std::apply([&__r]<typename... _Tp>(_Tp&&... __args) {
> +	    return ranges::to<_Cont>(std::forward<_Rg>(__r),
> +				     std::forward<_Tp>(__args)...);
> +	  }, std::move(_M_bound_args));
> +	}
> +    };
> +} // namespace __detail
> +/// @endcond
> +
> +  /// ranges::to adaptor for converting a range to a deduced container type.
> +  /**
> +   * @tparam _Cont A container template.
> +   * @param __args... Arguments to pass to the container constructor.
> +   * @since C++23
> +   *
> +   * This range adaptor returns a range adaptor closure object that converts
> +   * a range to a specialization of the `_Cont` class template. The specific
> +   * specialization of `_Cont` to be used is deduced automatically.
> +   *
> +   * For example, `some_view | std::ranges::to<std::vector>(Alloc<int>{})`
> +   * will convert the view to `std::vector<T, Alloc<T>>`, where `T` is the
> +   * view's value type, i.e. `std::ranges::range_value_t<decltype(some_view)>`.
> +   *
> +   * Additional constructor arguments for the container can be supplied, e.g.
> +   * `r | std::ranges::to<std::vector>(an_allocator)`.
> +   */
> +  template<template<typename...> typename _Cont, typename... _Args>
> +    constexpr __detail::_ToClosure2<_Cont, _Args...>
> +    to [[nodiscard]] (_Args&&... __args)
> +    { return {std::forward<_Args>(__args)...}; }
> +
> +} // namespace ranges
> +#endif // __cpp_lib_ranges_to_container
> +
>  _GLIBCXX_END_NAMESPACE_VERSION
> -} // namespace
> +} // namespace std
>  #endif // library concepts
>  #endif // C++2a
>  #endif /* _GLIBCXX_RANGES */
> diff --git a/libstdc++-v3/testsuite/std/ranges/conv/1.cc b/libstdc++-v3/testsuite/std/ranges/conv/1.cc
> new file mode 100644
> index 00000000000..0032cf32688
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/std/ranges/conv/1.cc
> @@ -0,0 +1,369 @@
> +// { dg-do run { target c++23 } }
> +
> +// C++23 26.5.7 Range conversions [range.utility.conv]
> +
> +#include <ranges>
> +#include <vector>
> +#include <string>
> +#include <deque>
> +#include <list>
> +#include <forward_list>
> +#include <map>
> +#include <testsuite_hooks.h>
> +#include <testsuite_allocator.h>
> +#include <testsuite_iterators.h>
> +
> +void
> +test_p1206r7_examples()
> +{
> +  using Alloc = __gnu_test::uneq_allocator<int>;
> +  const Alloc alloc(303);
> +  const std::map<int, const char*> m{{1, "one"}, {2, "two"}, {3, "three"}};
> +  namespace ranges = std::ranges;
> +
> +  auto l = std::views::iota(1, 10);
> +  // create a vector with the elements of l
> +  auto vec = ranges::to<std::vector<int>>(l); // or vector{std::from_range, l};
> +  //Specify an allocator
> +  auto b = ranges::to<std::vector<int, Alloc>>(l, alloc); // or vector{std::from_range, l, alloc};
> +  //deducing value_type
> +  auto c = ranges::to<std::vector>(l);
> +  // explicit conversion int -> long
> +  auto d = ranges::to<std::vector<long>>(l);
> +  //Supports converting associative container to sequence containers
> +  auto f = ranges::to<std::vector>(m);
> +  //Supports converting sequence containers to associative ones
> +  auto g = ranges::to<std::map>(f);
> +  //Pipe syntax
> +  auto g2 = l | ranges::views::take(42) | ranges::to<std::vector>();
> +  //Pipe syntax with allocator
> +  auto h = l | ranges::views::take(42) | ranges::to<std::vector>(alloc);
> +  //The pipe syntax also support specifying the type and conversions
> +  auto i = l | ranges::views::take(42) | ranges::to<std::vector<long>>();
> +  // Nested ranges
> +  std::list<std::forward_list<int>> lst = {{0, 1, 2, 3}, {4, 5, 6, 7}};
> +  auto vec1 = ranges::to<std::vector<std::vector<int>>>(lst);
> +  auto vec2 = ranges::to<std::vector<std::deque<double>>>(lst);
> +
> +  VERIFY( vec == std::vector<int>(std::ranges::begin(l), std::ranges::end(l)) );
> +  static_assert(std::is_same_v<decltype(b), std::vector<int, Alloc>>);
> +  VERIFY( b == (std::vector<int, Alloc>(vec.begin(), vec.end())) );
> +  VERIFY( b.get_allocator() == alloc );
> +  static_assert(std::is_same_v<decltype(c), std::vector<int>>);
> +  VERIFY( c == vec );
> +  static_assert(std::is_same_v<decltype(d), std::vector<long>>);
> +  VERIFY( d == std::vector<long>(vec.begin(), vec.end()) );
> +  VERIFY( g == m );
> +  static_assert(std::is_same_v<decltype(g2), std::vector<int>>);
> +  VERIFY( g2 == vec );
> +  static_assert(std::is_same_v<decltype(h), std::vector<int, Alloc>>);
> +  VERIFY( h == b );
> +  VERIFY( h.get_allocator() == alloc );
> +  VERIFY( i == d );
> +  static_assert(std::is_same_v<decltype(vec1), std::vector<std::vector<int>>>);
> +  VERIFY( vec1[1][1] == 5 );
> +  static_assert(std::is_same_v<decltype(vec2), std::vector<std::deque<double>>>);
> +  VERIFY( vec2[1][2] == 6.0 );
> +}
> +
> +void
> +test_example_1()
> +{
> +  using namespace std;
> +  using ranges::to;
> +
> +  // Example 1 from C++23 [range.utility.conv.general]
> +  string_view str = "the quick brown fox";
> +  auto words = views::split(str, ' ') | to<vector<string>>();
> +
> +  VERIFY( (is_same_v<decltype(words), vector<string>>) );
> +  VERIFY( words == vector<string>({"the", "quick", "brown", "fox"}) );
> +}
> +
> +template<typename C>
> +struct Cont1
> +{
> +  template<typename R, typename... Args>
> +    requires std::constructible_from<C, R&, Args&...>
> +    Cont1(R&& r, Args&&... args)
> +    : c(r, args...)
> +    { }
> +
> +  typename C::iterator begin();
> +  typename C::iterator end();
> +
> +  C c;
> +};
> +
> +void
> +test_2_1_1()
> +{
> +  // (2.1.1) constructible_from<C, R, Args...>
> +
> +  std::vector<int> v{1, 2, 3, 4};
> +  auto v2 = std::ranges::to<std::vector<int>>(v);
> +  static_assert(std::is_same_v<decltype(v2), decltype(v)>);
> +  VERIFY( v2 == v );
> +
> +  std::initializer_list<int> il{5, 6, 7, 8};
> +  v2 = std::ranges::to<std::vector<int>>(il);
> +  VERIFY( v2 == std::vector<int>(il) );
> +
> +  v2 = std::ranges::to<std::vector<int>>(il, std::allocator<int>{});
> +  VERIFY( v2 == std::vector<int>(il) );
> +
> +  using Alloc = __gnu_test::uneq_allocator<int>;
> +  using V = std::vector<int, Alloc>;
> +
> +  V v3({10, 11, 12, 13}, Alloc(14));
> +  auto v4 = std::ranges::to<V>(v3);
> +  static_assert(std::is_same_v<decltype(v4), V>);
> +  VERIFY( v4 == v3 );
> +  VERIFY( v4.get_allocator() == v3.get_allocator() );
> +
> +  auto v5 = std::ranges::to<V>(v3, Alloc(33));
> +  VERIFY( v5 == v3 );
> +  VERIFY( v5.get_allocator() == Alloc(33) );
> +
> +  auto v6 = std::ranges::to<V>(il, Alloc(44));
> +  VERIFY( v6 == V(il) );
> +  VERIFY( v6.get_allocator() == Alloc(44) );
> +
> +  auto c = std::ranges::to<Cont1<V>>(V{1, 2, 3});
> +  static_assert(std::is_same_v<decltype(c), Cont1<V>>);
> +  VERIFY( c.c == V({1, 2, 3}) );
> +
> +  auto c2 = std::ranges::to<Cont1<V>>(V{4, 5, 6}, Alloc(55));
> +  static_assert(std::is_same_v<decltype(c2), Cont1<V>>);
> +  VERIFY( c2.c == V({4, 5, 6}) );
> +  VERIFY( c2.c.get_allocator() == Alloc(55) );
> +
> +  auto c3 = std::ranges::to<Cont1<V>>(il, Alloc(66));
> +  static_assert(std::is_same_v<decltype(c2), Cont1<V>>);
> +  VERIFY( c3.c == V(v2.begin(), v2.end()) );
> +  VERIFY( c3.c.get_allocator() == Alloc(66) );
> +}
> +
> +template<typename C>
> +struct Cont2
> +{
> +  template<typename R, typename... Args>
> +    requires std::constructible_from<C, R&, Args&...>
> +    Cont2(std::from_range_t, R&& r, Args&&... args)
> +    : c(r, args...)
> +    { }
> +
> +  typename C::iterator begin();
> +  typename C::iterator end();
> +
> +  C c;
> +};
> +
> +void
> +test_2_1_2()
> +{
> +  // (2.1.2) constructible_from<C, from_range_t, R, Args...>
> +
> +  using Alloc = __gnu_test::uneq_allocator<int>;
> +  using V = std::vector<int, Alloc>;
> +  auto c = std::ranges::to<Cont2<V>>(V{1, 2, 3});
> +  static_assert(std::is_same_v<decltype(c), Cont2<V>>);
> +  VERIFY( c.c == V({1, 2, 3}) );
> +
> +  auto c2 = std::ranges::to<Cont2<V>>(V{4, 5, 6}, Alloc(7));
> +  static_assert(std::is_same_v<decltype(c2), Cont2<V>>);
> +  VERIFY( c2.c == V({4, 5, 6}) );
> +  VERIFY( c2.c.get_allocator() == Alloc(7) );
> +}
> +
> +template<typename C>
> +struct Cont3
> +{
> +  template<typename It, typename Sent, typename... Args>
> +    requires std::same_as<It, Sent>
> +    && std::constructible_from<C, It&, Sent&, Args&...>
> +    Cont3(It first, Sent last, Args&&... args)
> +    : c(first, last, args...)
> +    { }
> +
> +  typename C::iterator begin();
> +  typename C::iterator end();
> +
> +  C c;
> +};
> +
> +void
> +test_2_1_3()
> +{
> +  // (2.1.3) constructible_from<C, iterator_t<R>, sentinel_t<R<, Args...>
> +
> +  using Alloc = __gnu_test::uneq_allocator<int>;
> +  using V = std::vector<int, Alloc>;
> +
> +  std::list<unsigned> l{1u, 2u, 3u};
> +  auto c = std::ranges::to<Cont3<V>>(l);
> +  static_assert(std::is_same_v<decltype(c), Cont3<V>>);
> +  VERIFY( c.c == V(l.begin(), l.end()) );
> +
> +  std::list<long> l2{4l, 5l, 6l};
> +  auto c2 = std::ranges::to<Cont3<V>>(l2, Alloc(78));
> +  static_assert(std::is_same_v<decltype(c2), Cont3<V>>);
> +  VERIFY( c2.c == V(l2.begin(), l2.end()) );
> +  VERIFY( c2.c.get_allocator() == Alloc(78) );
> +}
> +
> +template<typename C, bool UsePushBack = true>
> +struct Cont4
> +{
> +  using value_type = typename C::value_type;
> +
> +  // Only support construction with no args or an allocator.
> +  // This forces the use of either push_back or insert to fill the container.
> +  Cont4() { }
> +  Cont4(typename C::allocator_type a) : c(a) { }
> +
> +  // Required to satisfy range
> +  typename C::iterator begin() { return c.begin(); }
> +  typename C::iterator end() { return c.end(); }
> +
> +  // Satisfying container-insertable requires either this ...
> +  template<typename T>
> +    requires UsePushBack
> +    && requires(C& c, T&& t) { c.push_back(std::forward<T>(t)); }
> +    void
> +    push_back(T&& t)
> +    {
> +      c.push_back(std::forward<T>(t));
> +      used_push_back = true;
> +    }
> +
> +  // ... or this:
> +  template<typename T>
> +    typename C::iterator
> +    insert(typename C::iterator, T&& t)
> +    {
> +      used_push_back = false;
> +      return c.insert(c.end(), std::forward<T>(t));
> +    }
> +
> +  // Required to satisfy reservable-container
> +  void
> +  reserve(typename C::size_type n) requires requires(C& c) { c.reserve(n); }
> +  {
> +    c.reserve(n);
> +    used_reserve = true;
> +  }
> +
> +  // Required to satisfy reservable-container
> +  auto size() const { return c.size(); }
> +
> +  // Required to satisfy reservable-container
> +  auto capacity() const requires requires(C& c) { c.capacity(); }
> +  { return c.capacity(); }
> +
> +  // Required to satisfy reservable-container
> +  auto max_size() const { return c.max_size(); }
> +
> +  C c;
> +  bool used_push_back = false;
> +  bool used_reserve = false;
> +};
> +
> +void
> +test_2_1_4()
> +{
> +  // (2.1.4) constructible_from<C, Args...> and
> +  // container-insertable<C, range_reference_t<R>>
> +
> +  using Alloc = __gnu_test::uneq_allocator<int>;
> +  using V = std::vector<int, Alloc>;
> +
> +  std::list<unsigned> l{1u, 2u, 3u};
> +  auto c = std::ranges::to<Cont4<V>>(l);
> +  static_assert(std::is_same_v<decltype(c), Cont4<V>>);
> +  VERIFY( c.c == V(l.begin(), l.end()) );
> +  VERIFY( c.used_push_back );
> +  VERIFY( c.used_reserve );
> +
> +  std::list<long> l2{4l, 5l, 6l};
> +  auto c2 = std::ranges::to<Cont4<V>>(l2, Alloc(78));
> +  static_assert(std::is_same_v<decltype(c2), Cont4<V>>);
> +  VERIFY( c2.c == V(l2.begin(), l2.end()) );
> +  VERIFY( c2.c.get_allocator() == Alloc(78) );
> +  VERIFY( c2.used_push_back );
> +  VERIFY( c2.used_reserve );
> +
> +  using Alloc2 = __gnu_test::uneq_allocator<short>;
> +  using List = std::list<short, Alloc2>;
> +  auto c3 = std::ranges::to<Cont4<List>>(c.c, Alloc2(99));
> +  static_assert(std::is_same_v<decltype(c3), Cont4<List>>);
> +  VERIFY( c3.c == List(l.begin(), l.end()) );
> +  VERIFY( c3.c.get_allocator() == Alloc(99) );
> +  VERIFY( c3.used_push_back );
> +  VERIFY( ! c3.used_reserve );
> +
> +  auto c4 = std::ranges::to<Cont4<List, false>>(c.c, Alloc2(111));
> +  static_assert(std::is_same_v<decltype(c4), Cont4<List, false>>);
> +  VERIFY( c4.c == List(l.begin(), l.end()) );
> +  VERIFY( c4.c.get_allocator() == Alloc(111) );
> +  VERIFY( ! c4.used_push_back );
> +  VERIFY( ! c4.used_reserve );
> +}
> +
> +void
> +test_2_2()
> +{
> +  // (2.2) input_range<range_reference_t<R>>
> +
> +  std::string s1[]{ "one", "two", "three", "four" };
> +  std::string s2[]{ "V", "VI", "VII", "VIII" };
> +  std::string s3[]{ "0x09", "0x0a", "0x0b", "0x0c" };
> +  using R = __gnu_test::test_input_range<std::string>;
> +  R input_ranges[]{R(s1), R(s2), R(s3)};
> +  __gnu_test::test_input_range<R> rr(input_ranges);
> +  namespace pmr = std::pmr;
> +  __gnu_test::memory_resource res;
> +#if _GLIBCXX_USE_CXX11_ABI
> +  auto vvs = std::ranges::to<pmr::vector<pmr::vector<pmr::string>>>(rr, &res);
> +  auto str_alloc = pmr::polymorphic_allocator<char>(&res);
> +#else
> +  auto vvs = std::ranges::to<pmr::vector<pmr::vector<std::string>>>(rr, &res);
> +  auto str_alloc = std::allocator<char>();
> +#endif
> +  VERIFY( vvs[1][1] == "VI" );
> +  VERIFY( vvs[2][2] == "0x0b" );
> +  VERIFY( vvs[0].get_allocator().resource() == &res );
> +  VERIFY( vvs[2][2].get_allocator() == str_alloc );
> +}
> +
> +void
> +test_lwg3984()
> +{
> +  std::vector<std::vector<int>> v;
> +  auto r = std::views::all(std::move(v));
> +  auto l = std::ranges::to<std::list<std::list<int>>>(r);
> +  VERIFY(l.empty());
> +}
> +
> +void
> +test_nodiscard()
> +{
> +  std::vector<int> v;
> +  std::ranges::to<std::vector<long>>(v); // { dg-warning "ignoring return" }
> +  std::ranges::to<std::vector>(v);       // { dg-warning "ignoring return" }
> +  std::ranges::to<std::vector<long>>();  // { dg-warning "ignoring return" }
> +  std::ranges::to<std::vector>();        // { dg-warning "ignoring return" }
> +}
> +
> +int main()
> +{
> +  test_p1206r7_examples();
> +  test_example_1();
> +  test_2_1_1();
> +  test_2_1_2();
> +  test_2_1_3();
> +  test_2_1_4();
> +  test_2_2();
> +  test_lwg3984();
> +  test_nodiscard();
> +}
> diff --git a/libstdc++-v3/testsuite/std/ranges/conv/2_neg.cc b/libstdc++-v3/testsuite/std/ranges/conv/2_neg.cc
> new file mode 100644
> index 00000000000..1e5f6f18408
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/std/ranges/conv/2_neg.cc
> @@ -0,0 +1,24 @@
> +// { dg-do compile { target c++23 } }
> +
> +// C++23 26.5.7 Range conversions [range.utility.conv]
> +
> +#include <ranges>
> +#include <vector>
> +#include <testsuite_allocator.h>
> +
> +void
> +test_2_1_5()
> +{
> +  // (2.1.5) Otherwise, the program is ill-formed.
> +
> +  using Alloc = __gnu_test::uneq_allocator<int>;
> +  using Vec = std::vector<int, Alloc>;
> +
> +  std::vector<int> v;
> +  (void) std::ranges::to<Vec>(v, v.get_allocator()); // { dg-error "here" }
> +
> +  (void) std::ranges::to<Vec>(Vec{}, 1, 2, 3, 4, 5, 6); // { dg-error "here" }
> +}
> +
> +// { dg-error "static assertion failed" "" { target *-*-* } 0 }
> +// { dg-prune-output "no matching function" }
> diff --git a/libstdc++-v3/testsuite/std/ranges/conv/version.cc b/libstdc++-v3/testsuite/std/ranges/conv/version.cc
> new file mode 100644
> index 00000000000..33736807eb4
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/std/ranges/conv/version.cc
> @@ -0,0 +1,19 @@
> +// { dg-do preprocess { target c++23 } }
> +// { dg-add-options no_pch }
> +
> +#include <ranges>
> +
> +#ifndef __cpp_lib_ranges_to_container
> +# error "Feature test macro for ranges_to_container is missing in <ranges>"
> +#elif __cpp_lib_ranges_to_container < 202202L
> +# error "Feature test macro for ranges_to_container has wrong value in <ranges>"
> +#endif
> +
> +#undef __cpp_lib_ranges_to_container
> +#include <version>
> +
> +#ifndef __cpp_lib_ranges_to_container
> +# error "Feature test macro for ranges_to_container is missing in <version>"
> +#elif __cpp_lib_ranges_to_container < 202202L
> +# error "Feature test macro for ranges_to_container has wrong value in <version>"
> +#endif
> -- 
> 2.42.0
> 
>
Jonathan Wakely Nov. 30, 2023, 3:49 p.m. UTC | #3
On Wed, 29 Nov 2023 at 16:28, Patrick Palka wrote:
>
> On Thu, 23 Nov 2023, Jonathan Wakely wrote:
>
> > Here's the finished version of the std::ranges::to patch, which I've
> > pushed to trunk.
> >
> > Tested x86_64-linux.
> >
> > -- >8 --
> >
> > This adds the std::ranges::to functions for C++23. The rest of P1206R7
> > is not yet implemented, i.e. the new constructors taking the
> > std::from_range tag, and the new insert_range, assign_range, etc. member
> > functions. std::ranges::to works with the standard containers even
> > without the new constructors, so this is useful immediately.
> >
> > The __cpp_lib_ranges_to_container feature test macro can be defined now,
> > because that only indicates support for the changes in <ranges>, which
> > are implemented by this patch. The __cpp_lib_containers_ranges macro
> > will be defined once all containers support the new member functions.
> >
> > libstdc++-v3/ChangeLog:
> >
> >       PR libstdc++/111055
> >       * include/bits/ranges_base.h (from_range_t): Define new tag
> >       type.
> >       (from_range): Define new tag object.
> >       * include/bits/version.def (ranges_to_container): Define.
> >       * include/bits/version.h: Regenerate.
> >       * include/std/ranges (ranges::to): Define.
> >       * testsuite/std/ranges/conv/1.cc: New test.
> >       * testsuite/std/ranges/conv/2_neg.cc: New test.
> >       * testsuite/std/ranges/conv/version.cc: New test.
> > ---
> >  libstdc++-v3/include/bits/ranges_base.h       |   8 +-
> >  libstdc++-v3/include/bits/version.def         |  34 +-
> >  libstdc++-v3/include/bits/version.h           | 111 +++---
> >  libstdc++-v3/include/std/ranges               | 361 ++++++++++++++++-
> >  libstdc++-v3/testsuite/std/ranges/conv/1.cc   | 369 ++++++++++++++++++
> >  .../testsuite/std/ranges/conv/2_neg.cc        |  24 ++
> >  .../testsuite/std/ranges/conv/version.cc      |  19 +
> >  7 files changed, 866 insertions(+), 60 deletions(-)
> >  create mode 100644 libstdc++-v3/testsuite/std/ranges/conv/1.cc
> >  create mode 100644 libstdc++-v3/testsuite/std/ranges/conv/2_neg.cc
> >  create mode 100644 libstdc++-v3/testsuite/std/ranges/conv/version.cc
> >
> > diff --git a/libstdc++-v3/include/bits/ranges_base.h b/libstdc++-v3/include/bits/ranges_base.h
> > index 7fa43d1965a..1ca2c5ce2bb 100644
> > --- a/libstdc++-v3/include/bits/ranges_base.h
> > +++ b/libstdc++-v3/include/bits/ranges_base.h
> > @@ -37,6 +37,7 @@
> >  #include <bits/stl_iterator.h>
> >  #include <ext/numeric_traits.h>
> >  #include <bits/max_size_type.h>
> > +#include <bits/version.h>
> >
> >  #ifdef __cpp_lib_concepts
> >  namespace std _GLIBCXX_VISIBILITY(default)
> > @@ -1056,8 +1057,13 @@ namespace ranges
> >      using borrowed_iterator_t = __conditional_t<borrowed_range<_Range>,
> >                                               iterator_t<_Range>,
> >                                               dangling>;
> > -
> >  } // 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{};
> > +#endif
> > +
> >  _GLIBCXX_END_NAMESPACE_VERSION
> >  } // namespace std
> >  #endif // library concepts
> > diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def
> > index 605708dfee7..140777832ed 100644
> > --- a/libstdc++-v3/include/bits/version.def
> > +++ b/libstdc++-v3/include/bits/version.def
> > @@ -1439,19 +1439,21 @@ ftms = {
> >    };
> >  };
> >
> > -ftms = {
> > -  name = to_underlying;
> > -  values = {
> > -    v = 202102;
> > -    cxxmin = 23;
> > -  };
> > -};
> > +//ftms = {
> > +//  name = container_ranges;
> > +//  values = {
> > +//    v = 202202;
> > +//    cxxmin = 23;
> > +//    hosted = yes;
> > +//  };
> > +//};
> >
> >  ftms = {
> > -  name = unreachable;
> > +  name = ranges_to_container;
> >    values = {
> >      v = 202202;
> >      cxxmin = 23;
> > +    hosted = yes;
> >    };
> >  };
> >
> > @@ -1683,6 +1685,22 @@ ftms = {
> >    };
> >  };
> >
> > +ftms = {
> > +  name = to_underlying;
> > +  values = {
> > +    v = 202102;
> > +    cxxmin = 23;
> > +  };
> > +};
> > +
> > +ftms = {
> > +  name = unreachable;
> > +  values = {
> > +    v = 202202;
> > +    cxxmin = 23;
> > +  };
> > +};
> > +
> >  ftms = {
> >    name = fstream_native_handle;
> >    values = {
> > diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h
> > index cacd9375cab..1fb1d148459 100644
> > --- a/libstdc++-v3/include/bits/version.h
> > +++ b/libstdc++-v3/include/bits/version.h
> > @@ -1740,29 +1740,18 @@
> >  #endif /* !defined(__cpp_lib_reference_from_temporary) && defined(__glibcxx_want_reference_from_temporary) */
> >  #undef __glibcxx_want_reference_from_temporary
> >
> > -// from version.def line 1443
> > -#if !defined(__cpp_lib_to_underlying)
> > -# if (__cplusplus >= 202100L)
> > -#  define __glibcxx_to_underlying 202102L
> > -#  if defined(__glibcxx_want_all) || defined(__glibcxx_want_to_underlying)
> > -#   define __cpp_lib_to_underlying 202102L
> > +// from version.def line 1452
> > +#if !defined(__cpp_lib_ranges_to_container)
> > +# if (__cplusplus >= 202100L) && _GLIBCXX_HOSTED
> > +#  define __glibcxx_ranges_to_container 202202L
> > +#  if defined(__glibcxx_want_all) || defined(__glibcxx_want_ranges_to_container)
> > +#   define __cpp_lib_ranges_to_container 202202L
> >  #  endif
> >  # endif
> > -#endif /* !defined(__cpp_lib_to_underlying) && defined(__glibcxx_want_to_underlying) */
> > -#undef __glibcxx_want_to_underlying
> > +#endif /* !defined(__cpp_lib_ranges_to_container) && defined(__glibcxx_want_ranges_to_container) */
> > +#undef __glibcxx_want_ranges_to_container
> >
> > -// from version.def line 1451
> > -#if !defined(__cpp_lib_unreachable)
> > -# if (__cplusplus >= 202100L)
> > -#  define __glibcxx_unreachable 202202L
> > -#  if defined(__glibcxx_want_all) || defined(__glibcxx_want_unreachable)
> > -#   define __cpp_lib_unreachable 202202L
> > -#  endif
> > -# endif
> > -#endif /* !defined(__cpp_lib_unreachable) && defined(__glibcxx_want_unreachable) */
> > -#undef __glibcxx_want_unreachable
> > -
> > -// from version.def line 1459
> > +// from version.def line 1461
> >  #if !defined(__cpp_lib_ranges_zip)
> >  # if (__cplusplus >= 202100L)
> >  #  define __glibcxx_ranges_zip 202110L
> > @@ -2059,7 +2048,29 @@
> >  #endif /* !defined(__cpp_lib_string_resize_and_overwrite) && defined(__glibcxx_want_string_resize_and_overwrite) */
> >  #undef __glibcxx_want_string_resize_and_overwrite
> >
> > -// from version.def line 1687
> > +// from version.def line 1689
> > +#if !defined(__cpp_lib_to_underlying)
> > +# if (__cplusplus >= 202100L)
> > +#  define __glibcxx_to_underlying 202102L
> > +#  if defined(__glibcxx_want_all) || defined(__glibcxx_want_to_underlying)
> > +#   define __cpp_lib_to_underlying 202102L
> > +#  endif
> > +# endif
> > +#endif /* !defined(__cpp_lib_to_underlying) && defined(__glibcxx_want_to_underlying) */
> > +#undef __glibcxx_want_to_underlying
> > +
> > +// from version.def line 1697
> > +#if !defined(__cpp_lib_unreachable)
> > +# if (__cplusplus >= 202100L)
> > +#  define __glibcxx_unreachable 202202L
> > +#  if defined(__glibcxx_want_all) || defined(__glibcxx_want_unreachable)
> > +#   define __cpp_lib_unreachable 202202L
> > +#  endif
> > +# endif
> > +#endif /* !defined(__cpp_lib_unreachable) && defined(__glibcxx_want_unreachable) */
> > +#undef __glibcxx_want_unreachable
> > +
> > +// from version.def line 1705
> >  #if !defined(__cpp_lib_fstream_native_handle)
> >  # if (__cplusplus >  202302L) && _GLIBCXX_HOSTED
> >  #  define __glibcxx_fstream_native_handle 202306L
> > diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges
> > index 26d6c013ad0..63bea862c05 100644
> > --- a/libstdc++-v3/include/std/ranges
> > +++ b/libstdc++-v3/include/std/ranges
> > @@ -64,6 +64,7 @@
> >  #define __glibcxx_want_ranges_repeat
> >  #define __glibcxx_want_ranges_slide
> >  #define __glibcxx_want_ranges_stride
> > +#define __glibcxx_want_ranges_to_container
> >  #define __glibcxx_want_ranges_zip
> >  #include <bits/version.h>
> >
> > @@ -9213,8 +9214,366 @@ namespace views::__adaptor
> >
> >    namespace views = ranges::views;
> >
> > +#if __cpp_lib_ranges_to_container // C++ >= 23
> > +namespace ranges
> > +{
> > +/// @cond undocumented
> > +namespace __detail
> > +{
> > +  template<typename _Container>
> > +    constexpr bool __reservable_container
> > +      = sized_range<_Container>
> > +      && requires(_Container& __c, range_size_t<_Container> __n) {
> > +     __c.reserve(__n);
> > +     { __c.capacity() } -> same_as<decltype(__n)>;
> > +     { __c.max_size() } -> same_as<decltype(__n)>;
> > +      };
> > +
> > +  template<typename _Container, typename _Ref>
> > +    constexpr bool __container_insertable
> > +      = requires(_Container& __c, _Ref&& __ref) {
> > +     typename _Container::value_type;
> > +     requires (
> > +       requires { __c.push_back(std::forward<_Ref>(__ref)); }
> > +       || requires { __c.insert(__c.end(), std::forward<_Ref>(__ref)); }
> > +     );
> > +      };
> > +
> > +  template<typename _Ref, typename _Container>
> > +    constexpr auto
> > +    __container_inserter(_Container& __c)
> > +    {
> > +      if constexpr (requires { __c.push_back(std::declval<_Ref>()); })
> > +     return std::back_inserter(__c);
> > +      else
> > +     return std::inserter(__c, __c.end());
> > +    }
> > +
> > +  template<typename _Cont, typename _Range>
> > +    constexpr bool __toable = requires {
> > +      requires (!input_range<_Range>
>
> It seems this sholud be input_range<_Cont>?
>
> > +               || convertible_to<range_reference_t<_Range>,
> > +                                 range_value_t<_Cont>>);
> > +    };
> > +} // namespace __detail
> > +/// @endcond
> > +
> > +  /// Convert a range to a container.
> > +  /**
> > +   * @tparam _Cont A container type.
> > +   * @param __r A range that models the `input_range` concept.
> > +   * @param __args... Arguments to pass to the container constructor.
> > +   * @since C++23
> > +   *
> > +   * This function converts a range to the `_Cont` type.
> > +   *
> > +   * For example, `std::ranges::to<std::vector<int>>(some_view)`
> > +   * will convert the view to `std::vector<int>`.
> > +   *
> > +   * Additional constructor arguments for the container can be supplied after
> > +   * the input range argument, e.g.
> > +   * `std::ranges::to<std::vector<int, Alloc<int>>>(a_range, an_allocator)`.
> > +   */
> > +  template<typename _Cont, input_range _Rg, typename... _Args>
> > +    requires (!view<_Cont>)
> > +    constexpr _Cont
> > +    to [[nodiscard]] (_Rg&& __r, _Args&&... __args)
> > +    {
> > +      static_assert(!is_const_v<_Cont> && !is_volatile_v<_Cont>);
> > +      static_assert(is_class_v<_Cont>);
> > +
> > +      if constexpr (__detail::__toable<_Cont, _Rg>)
> > +     {
> > +       if constexpr (constructible_from<_Cont, _Rg, _Args...>)
> > +         return _Cont(std::forward<_Rg>(__r),
> > +                      std::forward<_Args>(__args)...);
> > +       else if constexpr (constructible_from<_Cont, from_range_t, _Rg, _Args...>)
> > +         return _Cont(from_range, std::forward<_Rg>(__r),
> > +                      std::forward<_Args>(__args)...);
> > +       else if constexpr (requires { common_range<_Rg>;
>
> Missing 'requires' before common_range?
>
> > +             typename __iter_category_t<iterator_t<_Rg>>;
> > +             requires derived_from<__iter_category_t<iterator_t<_Rg>>,
> > +                                   input_iterator_tag>;
> > +             requires constructible_from<_Cont, iterator_t<_Rg>,
> > +                                         sentinel_t<_Rg>, _Args...>;
> > +           })
> > +         return _Cont(ranges::begin(__r), ranges::end(__r),
> > +                      std::forward<_Args>(__args)...);
> > +       else
> > +         {
> > +           using __detail::__container_insertable;
> > +           using __detail::__reservable_container;
> > +           using _RefT = range_reference_t<_Rg>;
> > +           static_assert(constructible_from<_Cont, _Args...>);
> > +           static_assert(__container_insertable<_Cont, _RefT>);
> > +           _Cont __c(std::forward<_Args>(__args)...);
> > +           if constexpr (sized_range<_Rg> && __reservable_container<_Cont>)
> > +             __c.reserve(static_cast<range_size_t<_Cont>>(ranges::size(__r)));
> > +           auto __ins = __detail::__container_inserter<_RefT>(__c);
> > +           for (auto&& __e : __r)
> > +             *__ins++ = std::forward<decltype(__e)>(__e);
> > +           return __c;
> > +         }
> > +     }
> > +      else
> > +     {
> > +       static_assert(input_range<range_reference_t<_Rg>>);
> > +       // _GLIBCXX_RESOLVE_LIB_DEFECTS
> > +       // 3984. ranges::to's recursion branch may be ill-formed
> > +       return ranges::to<_Cont>(ref_view(__r) | views::transform(
> > +             []<typename _Elt>(_Elt&& __elem) {
> > +               using _ValT = range_value_t<_Cont>;
> > +               return ranges::to<_ValT>(std::forward<_Elt>(__elem));
> > +             }), std::forward<_Args>(__args)...);
> > +     }
> > +    }
> > +
> > +/// @cond undocumented
> > +namespace __detail
> > +{
> > +  template<typename _Rg>
> > +    struct _InputIter
> > +    {
> > +      using iterator_category = input_iterator_tag;
> > +      using value_type = range_value_t<_Rg>;
> > +      using difference_type = ptrdiff_t;
> > +      using pointer = add_pointer_t<range_reference_t<_Rg>>;
> > +      using reference = range_reference_t<_Rg>;
> > +      reference operator*() const;
> > +      pointer operator->() const;
> > +      _InputIter& operator++();
> > +      _InputIter operator++(int);
> > +      bool operator==(const _InputIter&) const;
> > +    };
> > +
> > +#if 0
> > +  template<template<typename...> typename _Cont, typename _Rg,
> > +        typename... _Args>
> > +    concept __deduce_expr_1 = requires {
> > +      _Cont(std::declval<_Rg>(), std::declval<_Args>()...);
> > +    };
> > +
> > +  template<template<typename...> typename _Cont, typename _Rg,
> > +        typename... _Args>
> > +    concept __deduce_expr_2 = requires {
> > +      _Cont(from_range, std::declval<_Rg>(), std::declval<_Args>()...);
> > +    };
> > +
> > +  template<template<typename...> typename _Cont, typename _Rg,
> > +        typename... _Args>
> > +    concept __deduce_expr_3 = requires(_InputIter<_Rg> __i) {
> > +      _Cont(std::move(__i), std::move(__i), std::declval<_Args>()...);
> > +    };
> > +#endif
> > +
> > +  template<template<typename...> typename _Cont, input_range _Rg,
> > +        typename... _Args>
> > +    using _DeduceExpr1
> > +      = decltype(_Cont(std::declval<_Rg>(), std::declval<_Args>()...));
> > +
> > +  template<template<typename...> typename _Cont, input_range _Rg,
> > +        typename... _Args>
> > +    using _DeduceExpr2
> > +      = decltype(_Cont(from_range, std::declval<_Rg>(),
> > +                    std::declval<_Args>()...));
> > +
> > +  template<template<typename...> typename _Cont, input_range _Rg,
> > +        typename... _Args>
> > +    using _DeduceExpr3
> > +      = decltype(_Cont(std::declval<_InputIter<_Rg>>(),
> > +                    std::declval<_InputIter<_Rg>>(),
> > +                    std::declval<_Args>()...));
> > +
> > +} // namespace __detail
> > +/// @endcond
> > +
> > +  template<template<typename...> typename _Cont, input_range _Rg,
> > +        typename... _Args>
> > +    constexpr auto
> > +    to [[nodiscard]] (_Rg&& __r, _Args&&... __args)
> > +    {
> > +      using __detail::_DeduceExpr1;
> > +      using __detail::_DeduceExpr2;
> > +      using __detail::_DeduceExpr3;
> > +      if constexpr (requires { typename _DeduceExpr1<_Cont, _Rg, _Args...>; })
> > +     return ranges::to<_DeduceExpr1<_Cont, _Rg, _Args...>>(
> > +         std::forward<_Rg>(__r), std::forward<_Args>(__args)...);
> > +      else if constexpr (requires { typename _DeduceExpr2<_Cont, _Rg, _Args...>; })
> > +     return ranges::to<_DeduceExpr2<_Cont, _Rg, _Args...>>(
> > +         std::forward<_Rg>(__r), std::forward<_Args>(__args)...);
> > +      else if constexpr (requires { typename _DeduceExpr3<_Cont, _Rg, _Args...>; })
> > +     return ranges::to<_DeduceExpr3<_Cont, _Rg, _Args...>>(
> > +         std::forward<_Rg>(__r), std::forward<_Args>(__args)...);
> > +      else
> > +     static_assert(false); // Cannot deduce container specialization.
> > +    }
> > +
> > +/// @cond undocumented
> > +namespace __detail
> > +{
> > +  template<typename _Cont, typename... _Args>
> > +    class _ToClosure
> > +    : public views::__adaptor::_RangeAdaptorClosure<_ToClosure<_Cont, _Args...>>
> > +    {
> > +      tuple<decay_t<_Args>...> _M_bound_args;
> > +
> > +    public:
> > +      _ToClosure(_Args&&... __args)
>
> Missing constexpr?
>
> > +      : _M_bound_args(std::forward<_Args>(__args)...)
> > +      { }
> > +
> > +      // TODO: use explicit object functions ("deducing this").
> > +
> > +      template<typename _Rg>
> > +     constexpr auto
> > +     operator()(_Rg&& __r) &
> > +     {
> > +       return std::apply([&__r]<typename... _Tp>(_Tp&&... __args) {
> > +         return ranges::to<_Cont>(std::forward<_Rg>(__r),
> > +                                  std::forward<_Tp>(__args)...);
> > +       }, _M_bound_args);
> > +     }
> > +
> > +      template<typename _Rg>
> > +     constexpr auto
> > +     operator()(_Rg&& __r) const &
> > +     {
> > +       return std::apply([&__r]<typename... _Tp>(_Tp&&... __args) {
> > +         return ranges::to<_Cont>(std::forward<_Rg>(__r),
> > +                                  std::forward<_Tp>(__args)...);
> > +       }, _M_bound_args);
> > +     }
> > +
> > +      template<typename _Rg>
> > +     constexpr auto
> > +     operator()(_Rg&& __r) &&
> > +     {
> > +       return std::apply([&__r]<typename... _Tp>(_Tp&&... __args) {
> > +         return ranges::to<_Cont>(std::forward<_Rg>(__r),
> > +                                  std::forward<_Tp>(__args)...);
> > +       }, std::move(_M_bound_args));
> > +     }
> > +
> > +      template<typename _Rg>
> > +     constexpr auto
> > +     operator()(_Rg&& __r) const &&
> > +     {
> > +       return std::apply([&__r]<typename... _Tp>(_Tp&&... __args) {
> > +         return ranges::to<_Cont>(std::forward<_Rg>(__r),
> > +                                  std::forward<_Tp>(__args)...);
> > +       }, std::move(_M_bound_args));
> > +     }
> > +    };
> > +} // namespace __detail
> > +/// @endcond
> > +
> > +  /// ranges::to adaptor for converting a range to a container type
> > +  /**
> > +   * @tparam _Cont A container type.
> > +   * @param __args... Arguments to pass to the container constructor.
> > +   * @since C++23
> > +   *
> > +   * This range adaptor returns a range adaptor closure object that converts
> > +   * a range to the `_Cont` type.
> > +   *
> > +   * For example, `some_view | std::ranges::to<std::vector<int>>()`
> > +   * will convert the view to `std::vector<int>`.
> > +   *
> > +   * Additional constructor arguments for the container can be supplied, e.g.
> > +   * `r | std::ranges::to<std::vector<int, Alloc<int>>>(an_allocator)`.
> > +   */
> > +  template<typename _Cont, typename... _Args>
> > +    requires (!view<_Cont>)
> > +    constexpr __detail::_ToClosure<_Cont, _Args...>
> > +    to [[nodiscard]] (_Args&&... __args)
> > +    { return {std::forward<_Args>(__args)...}; }
> > +
> > +/// @cond undocumented
> > +namespace __detail
> > +{
> > +  template<template<typename...> typename _Cont, typename... _Args>
> > +    class _ToClosure2
> > +    : public views::__adaptor::_RangeAdaptorClosure<_ToClosure2<_Cont, _Args...>>
> > +    {
> > +      tuple<decay_t<_Args>...> _M_bound_args;
> > +
> > +    public:
> > +      _ToClosure2(_Args&&... __args)
>
> Same here.

Thanks, those are all fixed at r14-6020-g18d8a50a042a7f

>
> Would it be possible to use _RangeAdaptor instead of _RangeAdaptorClosure to
> leverage the existing partial application/forwarding code?  IIUC we'd need to
> allow omitting the _S_arity member to mean accept any number of arguments.
> I can work on that if anything.

Sure, that might be an improvement. Thanks!
diff mbox series

Patch

diff --git a/libstdc++-v3/include/bits/ranges_base.h b/libstdc++-v3/include/bits/ranges_base.h
index 7fa43d1965a..1ca2c5ce2bb 100644
--- a/libstdc++-v3/include/bits/ranges_base.h
+++ b/libstdc++-v3/include/bits/ranges_base.h
@@ -37,6 +37,7 @@ 
 #include <bits/stl_iterator.h>
 #include <ext/numeric_traits.h>
 #include <bits/max_size_type.h>
+#include <bits/version.h>
 
 #ifdef __cpp_lib_concepts
 namespace std _GLIBCXX_VISIBILITY(default)
@@ -1056,8 +1057,13 @@  namespace ranges
     using borrowed_iterator_t = __conditional_t<borrowed_range<_Range>,
 						iterator_t<_Range>,
 						dangling>;
-
 } // 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{};
+#endif
+
 _GLIBCXX_END_NAMESPACE_VERSION
 } // namespace std
 #endif // library concepts
diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def
index 605708dfee7..140777832ed 100644
--- a/libstdc++-v3/include/bits/version.def
+++ b/libstdc++-v3/include/bits/version.def
@@ -1439,19 +1439,21 @@  ftms = {
   };
 };
 
-ftms = {
-  name = to_underlying;
-  values = {
-    v = 202102;
-    cxxmin = 23;
-  };
-};
+//ftms = {
+//  name = container_ranges;
+//  values = {
+//    v = 202202;
+//    cxxmin = 23;
+//    hosted = yes;
+//  };
+//};
 
 ftms = {
-  name = unreachable;
+  name = ranges_to_container;
   values = {
     v = 202202;
     cxxmin = 23;
+    hosted = yes;
   };
 };
 
@@ -1683,6 +1685,22 @@  ftms = {
   };
 };
 
+ftms = {
+  name = to_underlying;
+  values = {
+    v = 202102;
+    cxxmin = 23;
+  };
+};
+
+ftms = {
+  name = unreachable;
+  values = {
+    v = 202202;
+    cxxmin = 23;
+  };
+};
+
 ftms = {
   name = fstream_native_handle;
   values = {
diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h
index cacd9375cab..1fb1d148459 100644
--- a/libstdc++-v3/include/bits/version.h
+++ b/libstdc++-v3/include/bits/version.h
@@ -1740,29 +1740,18 @@ 
 #endif /* !defined(__cpp_lib_reference_from_temporary) && defined(__glibcxx_want_reference_from_temporary) */
 #undef __glibcxx_want_reference_from_temporary
 
-// from version.def line 1443
-#if !defined(__cpp_lib_to_underlying)
-# if (__cplusplus >= 202100L)
-#  define __glibcxx_to_underlying 202102L
-#  if defined(__glibcxx_want_all) || defined(__glibcxx_want_to_underlying)
-#   define __cpp_lib_to_underlying 202102L
+// from version.def line 1452
+#if !defined(__cpp_lib_ranges_to_container)
+# if (__cplusplus >= 202100L) && _GLIBCXX_HOSTED
+#  define __glibcxx_ranges_to_container 202202L
+#  if defined(__glibcxx_want_all) || defined(__glibcxx_want_ranges_to_container)
+#   define __cpp_lib_ranges_to_container 202202L
 #  endif
 # endif
-#endif /* !defined(__cpp_lib_to_underlying) && defined(__glibcxx_want_to_underlying) */
-#undef __glibcxx_want_to_underlying
+#endif /* !defined(__cpp_lib_ranges_to_container) && defined(__glibcxx_want_ranges_to_container) */
+#undef __glibcxx_want_ranges_to_container
 
-// from version.def line 1451
-#if !defined(__cpp_lib_unreachable)
-# if (__cplusplus >= 202100L)
-#  define __glibcxx_unreachable 202202L
-#  if defined(__glibcxx_want_all) || defined(__glibcxx_want_unreachable)
-#   define __cpp_lib_unreachable 202202L
-#  endif
-# endif
-#endif /* !defined(__cpp_lib_unreachable) && defined(__glibcxx_want_unreachable) */
-#undef __glibcxx_want_unreachable
-
-// from version.def line 1459
+// from version.def line 1461
 #if !defined(__cpp_lib_ranges_zip)
 # if (__cplusplus >= 202100L)
 #  define __glibcxx_ranges_zip 202110L
@@ -2059,7 +2048,29 @@ 
 #endif /* !defined(__cpp_lib_string_resize_and_overwrite) && defined(__glibcxx_want_string_resize_and_overwrite) */
 #undef __glibcxx_want_string_resize_and_overwrite
 
-// from version.def line 1687
+// from version.def line 1689
+#if !defined(__cpp_lib_to_underlying)
+# if (__cplusplus >= 202100L)
+#  define __glibcxx_to_underlying 202102L
+#  if defined(__glibcxx_want_all) || defined(__glibcxx_want_to_underlying)
+#   define __cpp_lib_to_underlying 202102L
+#  endif
+# endif
+#endif /* !defined(__cpp_lib_to_underlying) && defined(__glibcxx_want_to_underlying) */
+#undef __glibcxx_want_to_underlying
+
+// from version.def line 1697
+#if !defined(__cpp_lib_unreachable)
+# if (__cplusplus >= 202100L)
+#  define __glibcxx_unreachable 202202L
+#  if defined(__glibcxx_want_all) || defined(__glibcxx_want_unreachable)
+#   define __cpp_lib_unreachable 202202L
+#  endif
+# endif
+#endif /* !defined(__cpp_lib_unreachable) && defined(__glibcxx_want_unreachable) */
+#undef __glibcxx_want_unreachable
+
+// from version.def line 1705
 #if !defined(__cpp_lib_fstream_native_handle)
 # if (__cplusplus >  202302L) && _GLIBCXX_HOSTED
 #  define __glibcxx_fstream_native_handle 202306L
diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges
index 26d6c013ad0..63bea862c05 100644
--- a/libstdc++-v3/include/std/ranges
+++ b/libstdc++-v3/include/std/ranges
@@ -64,6 +64,7 @@ 
 #define __glibcxx_want_ranges_repeat
 #define __glibcxx_want_ranges_slide
 #define __glibcxx_want_ranges_stride
+#define __glibcxx_want_ranges_to_container
 #define __glibcxx_want_ranges_zip
 #include <bits/version.h>
 
@@ -9213,8 +9214,366 @@  namespace views::__adaptor
 
   namespace views = ranges::views;
 
+#if __cpp_lib_ranges_to_container // C++ >= 23
+namespace ranges
+{
+/// @cond undocumented
+namespace __detail
+{
+  template<typename _Container>
+    constexpr bool __reservable_container
+      = sized_range<_Container>
+      && requires(_Container& __c, range_size_t<_Container> __n) {
+	__c.reserve(__n);
+	{ __c.capacity() } -> same_as<decltype(__n)>;
+	{ __c.max_size() } -> same_as<decltype(__n)>;
+      };
+
+  template<typename _Container, typename _Ref>
+    constexpr bool __container_insertable
+      = requires(_Container& __c, _Ref&& __ref) {
+	typename _Container::value_type;
+	requires (
+	  requires { __c.push_back(std::forward<_Ref>(__ref)); }
+	  || requires { __c.insert(__c.end(), std::forward<_Ref>(__ref)); }
+	);
+      };
+
+  template<typename _Ref, typename _Container>
+    constexpr auto
+    __container_inserter(_Container& __c)
+    {
+      if constexpr (requires { __c.push_back(std::declval<_Ref>()); })
+	return std::back_inserter(__c);
+      else
+	return std::inserter(__c, __c.end());
+    }
+
+  template<typename _Cont, typename _Range>
+    constexpr bool __toable = requires {
+      requires (!input_range<_Range>
+		  || convertible_to<range_reference_t<_Range>,
+				    range_value_t<_Cont>>);
+    };
+} // namespace __detail
+/// @endcond
+
+  /// Convert a range to a container.
+  /**
+   * @tparam _Cont A container type.
+   * @param __r A range that models the `input_range` concept.
+   * @param __args... Arguments to pass to the container constructor.
+   * @since C++23
+   *
+   * This function converts a range to the `_Cont` type.
+   *
+   * For example, `std::ranges::to<std::vector<int>>(some_view)`
+   * will convert the view to `std::vector<int>`.
+   *
+   * Additional constructor arguments for the container can be supplied after
+   * the input range argument, e.g.
+   * `std::ranges::to<std::vector<int, Alloc<int>>>(a_range, an_allocator)`.
+   */
+  template<typename _Cont, input_range _Rg, typename... _Args>
+    requires (!view<_Cont>)
+    constexpr _Cont
+    to [[nodiscard]] (_Rg&& __r, _Args&&... __args)
+    {
+      static_assert(!is_const_v<_Cont> && !is_volatile_v<_Cont>);
+      static_assert(is_class_v<_Cont>);
+
+      if constexpr (__detail::__toable<_Cont, _Rg>)
+	{
+	  if constexpr (constructible_from<_Cont, _Rg, _Args...>)
+	    return _Cont(std::forward<_Rg>(__r),
+			 std::forward<_Args>(__args)...);
+	  else if constexpr (constructible_from<_Cont, from_range_t, _Rg, _Args...>)
+	    return _Cont(from_range, std::forward<_Rg>(__r),
+			 std::forward<_Args>(__args)...);
+	  else if constexpr (requires { common_range<_Rg>;
+		typename __iter_category_t<iterator_t<_Rg>>;
+		requires derived_from<__iter_category_t<iterator_t<_Rg>>,
+				      input_iterator_tag>;
+		requires constructible_from<_Cont, iterator_t<_Rg>,
+					    sentinel_t<_Rg>, _Args...>;
+	      })
+	    return _Cont(ranges::begin(__r), ranges::end(__r),
+			 std::forward<_Args>(__args)...);
+	  else
+	    {
+	      using __detail::__container_insertable;
+	      using __detail::__reservable_container;
+	      using _RefT = range_reference_t<_Rg>;
+	      static_assert(constructible_from<_Cont, _Args...>);
+	      static_assert(__container_insertable<_Cont, _RefT>);
+	      _Cont __c(std::forward<_Args>(__args)...);
+	      if constexpr (sized_range<_Rg> && __reservable_container<_Cont>)
+		__c.reserve(static_cast<range_size_t<_Cont>>(ranges::size(__r)));
+	      auto __ins = __detail::__container_inserter<_RefT>(__c);
+	      for (auto&& __e : __r)
+		*__ins++ = std::forward<decltype(__e)>(__e);
+	      return __c;
+	    }
+	}
+      else
+	{
+	  static_assert(input_range<range_reference_t<_Rg>>);
+	  // _GLIBCXX_RESOLVE_LIB_DEFECTS
+	  // 3984. ranges::to's recursion branch may be ill-formed
+	  return ranges::to<_Cont>(ref_view(__r) | views::transform(
+		[]<typename _Elt>(_Elt&& __elem) {
+		  using _ValT = range_value_t<_Cont>;
+		  return ranges::to<_ValT>(std::forward<_Elt>(__elem));
+		}), std::forward<_Args>(__args)...);
+	}
+    }
+
+/// @cond undocumented
+namespace __detail
+{
+  template<typename _Rg>
+    struct _InputIter
+    {
+      using iterator_category = input_iterator_tag;
+      using value_type = range_value_t<_Rg>;
+      using difference_type = ptrdiff_t;
+      using pointer = add_pointer_t<range_reference_t<_Rg>>;
+      using reference = range_reference_t<_Rg>;
+      reference operator*() const;
+      pointer operator->() const;
+      _InputIter& operator++();
+      _InputIter operator++(int);
+      bool operator==(const _InputIter&) const;
+    };
+
+#if 0
+  template<template<typename...> typename _Cont, typename _Rg,
+	   typename... _Args>
+    concept __deduce_expr_1 = requires {
+      _Cont(std::declval<_Rg>(), std::declval<_Args>()...);
+    };
+
+  template<template<typename...> typename _Cont, typename _Rg,
+	   typename... _Args>
+    concept __deduce_expr_2 = requires {
+      _Cont(from_range, std::declval<_Rg>(), std::declval<_Args>()...);
+    };
+
+  template<template<typename...> typename _Cont, typename _Rg,
+	   typename... _Args>
+    concept __deduce_expr_3 = requires(_InputIter<_Rg> __i) {
+      _Cont(std::move(__i), std::move(__i), std::declval<_Args>()...);
+    };
+#endif
+
+  template<template<typename...> typename _Cont, input_range _Rg,
+	   typename... _Args>
+    using _DeduceExpr1
+      = decltype(_Cont(std::declval<_Rg>(), std::declval<_Args>()...));
+
+  template<template<typename...> typename _Cont, input_range _Rg,
+	   typename... _Args>
+    using _DeduceExpr2
+      = decltype(_Cont(from_range, std::declval<_Rg>(),
+		       std::declval<_Args>()...));
+
+  template<template<typename...> typename _Cont, input_range _Rg,
+	   typename... _Args>
+    using _DeduceExpr3
+      = decltype(_Cont(std::declval<_InputIter<_Rg>>(),
+		       std::declval<_InputIter<_Rg>>(),
+		       std::declval<_Args>()...));
+
+} // namespace __detail
+/// @endcond
+
+  template<template<typename...> typename _Cont, input_range _Rg,
+	   typename... _Args>
+    constexpr auto
+    to [[nodiscard]] (_Rg&& __r, _Args&&... __args)
+    {
+      using __detail::_DeduceExpr1;
+      using __detail::_DeduceExpr2;
+      using __detail::_DeduceExpr3;
+      if constexpr (requires { typename _DeduceExpr1<_Cont, _Rg, _Args...>; })
+	return ranges::to<_DeduceExpr1<_Cont, _Rg, _Args...>>(
+	    std::forward<_Rg>(__r), std::forward<_Args>(__args)...);
+      else if constexpr (requires { typename _DeduceExpr2<_Cont, _Rg, _Args...>; })
+	return ranges::to<_DeduceExpr2<_Cont, _Rg, _Args...>>(
+	    std::forward<_Rg>(__r), std::forward<_Args>(__args)...);
+      else if constexpr (requires { typename _DeduceExpr3<_Cont, _Rg, _Args...>; })
+	return ranges::to<_DeduceExpr3<_Cont, _Rg, _Args...>>(
+	    std::forward<_Rg>(__r), std::forward<_Args>(__args)...);
+      else
+	static_assert(false); // Cannot deduce container specialization.
+    }
+
+/// @cond undocumented
+namespace __detail
+{
+  template<typename _Cont, typename... _Args>
+    class _ToClosure
+    : public views::__adaptor::_RangeAdaptorClosure<_ToClosure<_Cont, _Args...>>
+    {
+      tuple<decay_t<_Args>...> _M_bound_args;
+
+    public:
+      _ToClosure(_Args&&... __args)
+      : _M_bound_args(std::forward<_Args>(__args)...)
+      { }
+
+      // TODO: use explicit object functions ("deducing this").
+
+      template<typename _Rg>
+	constexpr auto
+	operator()(_Rg&& __r) &
+	{
+	  return std::apply([&__r]<typename... _Tp>(_Tp&&... __args) {
+	    return ranges::to<_Cont>(std::forward<_Rg>(__r),
+				     std::forward<_Tp>(__args)...);
+	  }, _M_bound_args);
+	}
+
+      template<typename _Rg>
+	constexpr auto
+	operator()(_Rg&& __r) const &
+	{
+	  return std::apply([&__r]<typename... _Tp>(_Tp&&... __args) {
+	    return ranges::to<_Cont>(std::forward<_Rg>(__r),
+				     std::forward<_Tp>(__args)...);
+	  }, _M_bound_args);
+	}
+
+      template<typename _Rg>
+	constexpr auto
+	operator()(_Rg&& __r) &&
+	{
+	  return std::apply([&__r]<typename... _Tp>(_Tp&&... __args) {
+	    return ranges::to<_Cont>(std::forward<_Rg>(__r),
+				     std::forward<_Tp>(__args)...);
+	  }, std::move(_M_bound_args));
+	}
+
+      template<typename _Rg>
+	constexpr auto
+	operator()(_Rg&& __r) const &&
+	{
+	  return std::apply([&__r]<typename... _Tp>(_Tp&&... __args) {
+	    return ranges::to<_Cont>(std::forward<_Rg>(__r),
+				     std::forward<_Tp>(__args)...);
+	  }, std::move(_M_bound_args));
+	}
+    };
+} // namespace __detail
+/// @endcond
+
+  /// ranges::to adaptor for converting a range to a container type
+  /**
+   * @tparam _Cont A container type.
+   * @param __args... Arguments to pass to the container constructor.
+   * @since C++23
+   *
+   * This range adaptor returns a range adaptor closure object that converts
+   * a range to the `_Cont` type.
+   *
+   * For example, `some_view | std::ranges::to<std::vector<int>>()`
+   * will convert the view to `std::vector<int>`.
+   *
+   * Additional constructor arguments for the container can be supplied, e.g.
+   * `r | std::ranges::to<std::vector<int, Alloc<int>>>(an_allocator)`.
+   */
+  template<typename _Cont, typename... _Args>
+    requires (!view<_Cont>)
+    constexpr __detail::_ToClosure<_Cont, _Args...>
+    to [[nodiscard]] (_Args&&... __args)
+    { return {std::forward<_Args>(__args)...}; }
+
+/// @cond undocumented
+namespace __detail
+{
+  template<template<typename...> typename _Cont, typename... _Args>
+    class _ToClosure2
+    : public views::__adaptor::_RangeAdaptorClosure<_ToClosure2<_Cont, _Args...>>
+    {
+      tuple<decay_t<_Args>...> _M_bound_args;
+
+    public:
+      _ToClosure2(_Args&&... __args)
+      : _M_bound_args(std::forward<_Args>(__args)...)
+      { }
+
+      // TODO: use explicit object functions ("deducing this").
+
+      template<typename _Rg>
+	constexpr auto
+	operator()(_Rg&& __r) &
+	{
+	  return std::apply([&__r]<typename... _Tp>(_Tp&&... __args) {
+	    return ranges::to<_Cont>(std::forward<_Rg>(__r),
+				     std::forward<_Tp>(__args)...);
+	  }, _M_bound_args);
+	}
+
+      template<typename _Rg>
+	constexpr auto
+	operator()(_Rg&& __r) const &
+	{
+	  return std::apply([&__r]<typename... _Tp>(_Tp&&... __args) {
+	    return ranges::to<_Cont>(std::forward<_Rg>(__r),
+				     std::forward<_Tp>(__args)...);
+	  }, _M_bound_args);
+	}
+
+      template<typename _Rg>
+	constexpr auto
+	operator()(_Rg&& __r) &&
+	{
+	  return std::apply([&__r]<typename... _Tp>(_Tp&&... __args) {
+	    return ranges::to<_Cont>(std::forward<_Rg>(__r),
+				     std::forward<_Tp>(__args)...);
+	  }, std::move(_M_bound_args));
+	}
+
+      template<typename _Rg>
+	constexpr auto
+	operator()(_Rg&& __r) const &&
+	{
+	  return std::apply([&__r]<typename... _Tp>(_Tp&&... __args) {
+	    return ranges::to<_Cont>(std::forward<_Rg>(__r),
+				     std::forward<_Tp>(__args)...);
+	  }, std::move(_M_bound_args));
+	}
+    };
+} // namespace __detail
+/// @endcond
+
+  /// ranges::to adaptor for converting a range to a deduced container type.
+  /**
+   * @tparam _Cont A container template.
+   * @param __args... Arguments to pass to the container constructor.
+   * @since C++23
+   *
+   * This range adaptor returns a range adaptor closure object that converts
+   * a range to a specialization of the `_Cont` class template. The specific
+   * specialization of `_Cont` to be used is deduced automatically.
+   *
+   * For example, `some_view | std::ranges::to<std::vector>(Alloc<int>{})`
+   * will convert the view to `std::vector<T, Alloc<T>>`, where `T` is the
+   * view's value type, i.e. `std::ranges::range_value_t<decltype(some_view)>`.
+   *
+   * Additional constructor arguments for the container can be supplied, e.g.
+   * `r | std::ranges::to<std::vector>(an_allocator)`.
+   */
+  template<template<typename...> typename _Cont, typename... _Args>
+    constexpr __detail::_ToClosure2<_Cont, _Args...>
+    to [[nodiscard]] (_Args&&... __args)
+    { return {std::forward<_Args>(__args)...}; }
+
+} // namespace ranges
+#endif // __cpp_lib_ranges_to_container
+
 _GLIBCXX_END_NAMESPACE_VERSION
-} // namespace
+} // namespace std
 #endif // library concepts
 #endif // C++2a
 #endif /* _GLIBCXX_RANGES */
diff --git a/libstdc++-v3/testsuite/std/ranges/conv/1.cc b/libstdc++-v3/testsuite/std/ranges/conv/1.cc
new file mode 100644
index 00000000000..0032cf32688
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/ranges/conv/1.cc
@@ -0,0 +1,369 @@ 
+// { dg-do run { target c++23 } }
+
+// C++23 26.5.7 Range conversions [range.utility.conv]
+
+#include <ranges>
+#include <vector>
+#include <string>
+#include <deque>
+#include <list>
+#include <forward_list>
+#include <map>
+#include <testsuite_hooks.h>
+#include <testsuite_allocator.h>
+#include <testsuite_iterators.h>
+
+void
+test_p1206r7_examples()
+{
+  using Alloc = __gnu_test::uneq_allocator<int>;
+  const Alloc alloc(303);
+  const std::map<int, const char*> m{{1, "one"}, {2, "two"}, {3, "three"}};
+  namespace ranges = std::ranges;
+
+  auto l = std::views::iota(1, 10);
+  // create a vector with the elements of l
+  auto vec = ranges::to<std::vector<int>>(l); // or vector{std::from_range, l};
+  //Specify an allocator
+  auto b = ranges::to<std::vector<int, Alloc>>(l, alloc); // or vector{std::from_range, l, alloc};
+  //deducing value_type
+  auto c = ranges::to<std::vector>(l);
+  // explicit conversion int -> long
+  auto d = ranges::to<std::vector<long>>(l);
+  //Supports converting associative container to sequence containers
+  auto f = ranges::to<std::vector>(m);
+  //Supports converting sequence containers to associative ones
+  auto g = ranges::to<std::map>(f);
+  //Pipe syntax
+  auto g2 = l | ranges::views::take(42) | ranges::to<std::vector>();
+  //Pipe syntax with allocator
+  auto h = l | ranges::views::take(42) | ranges::to<std::vector>(alloc);
+  //The pipe syntax also support specifying the type and conversions
+  auto i = l | ranges::views::take(42) | ranges::to<std::vector<long>>();
+  // Nested ranges
+  std::list<std::forward_list<int>> lst = {{0, 1, 2, 3}, {4, 5, 6, 7}};
+  auto vec1 = ranges::to<std::vector<std::vector<int>>>(lst);
+  auto vec2 = ranges::to<std::vector<std::deque<double>>>(lst);
+
+  VERIFY( vec == std::vector<int>(std::ranges::begin(l), std::ranges::end(l)) );
+  static_assert(std::is_same_v<decltype(b), std::vector<int, Alloc>>);
+  VERIFY( b == (std::vector<int, Alloc>(vec.begin(), vec.end())) );
+  VERIFY( b.get_allocator() == alloc );
+  static_assert(std::is_same_v<decltype(c), std::vector<int>>);
+  VERIFY( c == vec );
+  static_assert(std::is_same_v<decltype(d), std::vector<long>>);
+  VERIFY( d == std::vector<long>(vec.begin(), vec.end()) );
+  VERIFY( g == m );
+  static_assert(std::is_same_v<decltype(g2), std::vector<int>>);
+  VERIFY( g2 == vec );
+  static_assert(std::is_same_v<decltype(h), std::vector<int, Alloc>>);
+  VERIFY( h == b );
+  VERIFY( h.get_allocator() == alloc );
+  VERIFY( i == d );
+  static_assert(std::is_same_v<decltype(vec1), std::vector<std::vector<int>>>);
+  VERIFY( vec1[1][1] == 5 );
+  static_assert(std::is_same_v<decltype(vec2), std::vector<std::deque<double>>>);
+  VERIFY( vec2[1][2] == 6.0 );
+}
+
+void
+test_example_1()
+{
+  using namespace std;
+  using ranges::to;
+
+  // Example 1 from C++23 [range.utility.conv.general]
+  string_view str = "the quick brown fox";
+  auto words = views::split(str, ' ') | to<vector<string>>();
+
+  VERIFY( (is_same_v<decltype(words), vector<string>>) );
+  VERIFY( words == vector<string>({"the", "quick", "brown", "fox"}) );
+}
+
+template<typename C>
+struct Cont1
+{
+  template<typename R, typename... Args>
+    requires std::constructible_from<C, R&, Args&...>
+    Cont1(R&& r, Args&&... args)
+    : c(r, args...)
+    { }
+
+  typename C::iterator begin();
+  typename C::iterator end();
+
+  C c;
+};
+
+void
+test_2_1_1()
+{
+  // (2.1.1) constructible_from<C, R, Args...>
+
+  std::vector<int> v{1, 2, 3, 4};
+  auto v2 = std::ranges::to<std::vector<int>>(v);
+  static_assert(std::is_same_v<decltype(v2), decltype(v)>);
+  VERIFY( v2 == v );
+
+  std::initializer_list<int> il{5, 6, 7, 8};
+  v2 = std::ranges::to<std::vector<int>>(il);
+  VERIFY( v2 == std::vector<int>(il) );
+
+  v2 = std::ranges::to<std::vector<int>>(il, std::allocator<int>{});
+  VERIFY( v2 == std::vector<int>(il) );
+
+  using Alloc = __gnu_test::uneq_allocator<int>;
+  using V = std::vector<int, Alloc>;
+
+  V v3({10, 11, 12, 13}, Alloc(14));
+  auto v4 = std::ranges::to<V>(v3);
+  static_assert(std::is_same_v<decltype(v4), V>);
+  VERIFY( v4 == v3 );
+  VERIFY( v4.get_allocator() == v3.get_allocator() );
+
+  auto v5 = std::ranges::to<V>(v3, Alloc(33));
+  VERIFY( v5 == v3 );
+  VERIFY( v5.get_allocator() == Alloc(33) );
+
+  auto v6 = std::ranges::to<V>(il, Alloc(44));
+  VERIFY( v6 == V(il) );
+  VERIFY( v6.get_allocator() == Alloc(44) );
+
+  auto c = std::ranges::to<Cont1<V>>(V{1, 2, 3});
+  static_assert(std::is_same_v<decltype(c), Cont1<V>>);
+  VERIFY( c.c == V({1, 2, 3}) );
+
+  auto c2 = std::ranges::to<Cont1<V>>(V{4, 5, 6}, Alloc(55));
+  static_assert(std::is_same_v<decltype(c2), Cont1<V>>);
+  VERIFY( c2.c == V({4, 5, 6}) );
+  VERIFY( c2.c.get_allocator() == Alloc(55) );
+
+  auto c3 = std::ranges::to<Cont1<V>>(il, Alloc(66));
+  static_assert(std::is_same_v<decltype(c2), Cont1<V>>);
+  VERIFY( c3.c == V(v2.begin(), v2.end()) );
+  VERIFY( c3.c.get_allocator() == Alloc(66) );
+}
+
+template<typename C>
+struct Cont2
+{
+  template<typename R, typename... Args>
+    requires std::constructible_from<C, R&, Args&...>
+    Cont2(std::from_range_t, R&& r, Args&&... args)
+    : c(r, args...)
+    { }
+
+  typename C::iterator begin();
+  typename C::iterator end();
+
+  C c;
+};
+
+void
+test_2_1_2()
+{
+  // (2.1.2) constructible_from<C, from_range_t, R, Args...>
+
+  using Alloc = __gnu_test::uneq_allocator<int>;
+  using V = std::vector<int, Alloc>;
+  auto c = std::ranges::to<Cont2<V>>(V{1, 2, 3});
+  static_assert(std::is_same_v<decltype(c), Cont2<V>>);
+  VERIFY( c.c == V({1, 2, 3}) );
+
+  auto c2 = std::ranges::to<Cont2<V>>(V{4, 5, 6}, Alloc(7));
+  static_assert(std::is_same_v<decltype(c2), Cont2<V>>);
+  VERIFY( c2.c == V({4, 5, 6}) );
+  VERIFY( c2.c.get_allocator() == Alloc(7) );
+}
+
+template<typename C>
+struct Cont3
+{
+  template<typename It, typename Sent, typename... Args>
+    requires std::same_as<It, Sent>
+    && std::constructible_from<C, It&, Sent&, Args&...>
+    Cont3(It first, Sent last, Args&&... args)
+    : c(first, last, args...)
+    { }
+
+  typename C::iterator begin();
+  typename C::iterator end();
+
+  C c;
+};
+
+void
+test_2_1_3()
+{
+  // (2.1.3) constructible_from<C, iterator_t<R>, sentinel_t<R<, Args...>
+
+  using Alloc = __gnu_test::uneq_allocator<int>;
+  using V = std::vector<int, Alloc>;
+
+  std::list<unsigned> l{1u, 2u, 3u};
+  auto c = std::ranges::to<Cont3<V>>(l);
+  static_assert(std::is_same_v<decltype(c), Cont3<V>>);
+  VERIFY( c.c == V(l.begin(), l.end()) );
+
+  std::list<long> l2{4l, 5l, 6l};
+  auto c2 = std::ranges::to<Cont3<V>>(l2, Alloc(78));
+  static_assert(std::is_same_v<decltype(c2), Cont3<V>>);
+  VERIFY( c2.c == V(l2.begin(), l2.end()) );
+  VERIFY( c2.c.get_allocator() == Alloc(78) );
+}
+
+template<typename C, bool UsePushBack = true>
+struct Cont4
+{
+  using value_type = typename C::value_type;
+
+  // Only support construction with no args or an allocator.
+  // This forces the use of either push_back or insert to fill the container.
+  Cont4() { }
+  Cont4(typename C::allocator_type a) : c(a) { }
+
+  // Required to satisfy range
+  typename C::iterator begin() { return c.begin(); }
+  typename C::iterator end() { return c.end(); }
+
+  // Satisfying container-insertable requires either this ...
+  template<typename T>
+    requires UsePushBack
+    && requires(C& c, T&& t) { c.push_back(std::forward<T>(t)); }
+    void
+    push_back(T&& t)
+    {
+      c.push_back(std::forward<T>(t));
+      used_push_back = true;
+    }
+
+  // ... or this:
+  template<typename T>
+    typename C::iterator
+    insert(typename C::iterator, T&& t)
+    {
+      used_push_back = false;
+      return c.insert(c.end(), std::forward<T>(t));
+    }
+
+  // Required to satisfy reservable-container
+  void
+  reserve(typename C::size_type n) requires requires(C& c) { c.reserve(n); }
+  {
+    c.reserve(n);
+    used_reserve = true;
+  }
+
+  // Required to satisfy reservable-container
+  auto size() const { return c.size(); }
+
+  // Required to satisfy reservable-container
+  auto capacity() const requires requires(C& c) { c.capacity(); }
+  { return c.capacity(); }
+
+  // Required to satisfy reservable-container
+  auto max_size() const { return c.max_size(); }
+
+  C c;
+  bool used_push_back = false;
+  bool used_reserve = false;
+};
+
+void
+test_2_1_4()
+{
+  // (2.1.4) constructible_from<C, Args...> and
+  // container-insertable<C, range_reference_t<R>>
+
+  using Alloc = __gnu_test::uneq_allocator<int>;
+  using V = std::vector<int, Alloc>;
+
+  std::list<unsigned> l{1u, 2u, 3u};
+  auto c = std::ranges::to<Cont4<V>>(l);
+  static_assert(std::is_same_v<decltype(c), Cont4<V>>);
+  VERIFY( c.c == V(l.begin(), l.end()) );
+  VERIFY( c.used_push_back );
+  VERIFY( c.used_reserve );
+
+  std::list<long> l2{4l, 5l, 6l};
+  auto c2 = std::ranges::to<Cont4<V>>(l2, Alloc(78));
+  static_assert(std::is_same_v<decltype(c2), Cont4<V>>);
+  VERIFY( c2.c == V(l2.begin(), l2.end()) );
+  VERIFY( c2.c.get_allocator() == Alloc(78) );
+  VERIFY( c2.used_push_back );
+  VERIFY( c2.used_reserve );
+
+  using Alloc2 = __gnu_test::uneq_allocator<short>;
+  using List = std::list<short, Alloc2>;
+  auto c3 = std::ranges::to<Cont4<List>>(c.c, Alloc2(99));
+  static_assert(std::is_same_v<decltype(c3), Cont4<List>>);
+  VERIFY( c3.c == List(l.begin(), l.end()) );
+  VERIFY( c3.c.get_allocator() == Alloc(99) );
+  VERIFY( c3.used_push_back );
+  VERIFY( ! c3.used_reserve );
+
+  auto c4 = std::ranges::to<Cont4<List, false>>(c.c, Alloc2(111));
+  static_assert(std::is_same_v<decltype(c4), Cont4<List, false>>);
+  VERIFY( c4.c == List(l.begin(), l.end()) );
+  VERIFY( c4.c.get_allocator() == Alloc(111) );
+  VERIFY( ! c4.used_push_back );
+  VERIFY( ! c4.used_reserve );
+}
+
+void
+test_2_2()
+{
+  // (2.2) input_range<range_reference_t<R>>
+
+  std::string s1[]{ "one", "two", "three", "four" };
+  std::string s2[]{ "V", "VI", "VII", "VIII" };
+  std::string s3[]{ "0x09", "0x0a", "0x0b", "0x0c" };
+  using R = __gnu_test::test_input_range<std::string>;
+  R input_ranges[]{R(s1), R(s2), R(s3)};
+  __gnu_test::test_input_range<R> rr(input_ranges);
+  namespace pmr = std::pmr;
+  __gnu_test::memory_resource res;
+#if _GLIBCXX_USE_CXX11_ABI
+  auto vvs = std::ranges::to<pmr::vector<pmr::vector<pmr::string>>>(rr, &res);
+  auto str_alloc = pmr::polymorphic_allocator<char>(&res);
+#else
+  auto vvs = std::ranges::to<pmr::vector<pmr::vector<std::string>>>(rr, &res);
+  auto str_alloc = std::allocator<char>();
+#endif
+  VERIFY( vvs[1][1] == "VI" );
+  VERIFY( vvs[2][2] == "0x0b" );
+  VERIFY( vvs[0].get_allocator().resource() == &res );
+  VERIFY( vvs[2][2].get_allocator() == str_alloc );
+}
+
+void
+test_lwg3984()
+{
+  std::vector<std::vector<int>> v;
+  auto r = std::views::all(std::move(v));
+  auto l = std::ranges::to<std::list<std::list<int>>>(r);
+  VERIFY(l.empty());
+}
+
+void
+test_nodiscard()
+{
+  std::vector<int> v;
+  std::ranges::to<std::vector<long>>(v); // { dg-warning "ignoring return" }
+  std::ranges::to<std::vector>(v);       // { dg-warning "ignoring return" }
+  std::ranges::to<std::vector<long>>();  // { dg-warning "ignoring return" }
+  std::ranges::to<std::vector>();        // { dg-warning "ignoring return" }
+}
+
+int main()
+{
+  test_p1206r7_examples();
+  test_example_1();
+  test_2_1_1();
+  test_2_1_2();
+  test_2_1_3();
+  test_2_1_4();
+  test_2_2();
+  test_lwg3984();
+  test_nodiscard();
+}
diff --git a/libstdc++-v3/testsuite/std/ranges/conv/2_neg.cc b/libstdc++-v3/testsuite/std/ranges/conv/2_neg.cc
new file mode 100644
index 00000000000..1e5f6f18408
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/ranges/conv/2_neg.cc
@@ -0,0 +1,24 @@ 
+// { dg-do compile { target c++23 } }
+
+// C++23 26.5.7 Range conversions [range.utility.conv]
+
+#include <ranges>
+#include <vector>
+#include <testsuite_allocator.h>
+
+void
+test_2_1_5()
+{
+  // (2.1.5) Otherwise, the program is ill-formed.
+
+  using Alloc = __gnu_test::uneq_allocator<int>;
+  using Vec = std::vector<int, Alloc>;
+
+  std::vector<int> v;
+  (void) std::ranges::to<Vec>(v, v.get_allocator()); // { dg-error "here" }
+
+  (void) std::ranges::to<Vec>(Vec{}, 1, 2, 3, 4, 5, 6); // { dg-error "here" }
+}
+
+// { dg-error "static assertion failed" "" { target *-*-* } 0 }
+// { dg-prune-output "no matching function" }
diff --git a/libstdc++-v3/testsuite/std/ranges/conv/version.cc b/libstdc++-v3/testsuite/std/ranges/conv/version.cc
new file mode 100644
index 00000000000..33736807eb4
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/ranges/conv/version.cc
@@ -0,0 +1,19 @@ 
+// { dg-do preprocess { target c++23 } }
+// { dg-add-options no_pch }
+
+#include <ranges>
+
+#ifndef __cpp_lib_ranges_to_container
+# error "Feature test macro for ranges_to_container is missing in <ranges>"
+#elif __cpp_lib_ranges_to_container < 202202L
+# error "Feature test macro for ranges_to_container has wrong value in <ranges>"
+#endif
+
+#undef __cpp_lib_ranges_to_container
+#include <version>
+
+#ifndef __cpp_lib_ranges_to_container
+# error "Feature test macro for ranges_to_container is missing in <version>"
+#elif __cpp_lib_ranges_to_container < 202202L
+# error "Feature test macro for ranges_to_container has wrong value in <version>"
+#endif