public inbox for libstdc++@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH] libstdc++: Define std::ranges::to for C++23 (P1206R7) [PR111055]
@ 2023-11-17 15:49 Jonathan Wakely
  2023-11-23 17:51 ` [committed v2] " Jonathan Wakely
  0 siblings, 1 reply; 5+ messages in thread
From: Jonathan Wakely @ 2023-11-17 15:49 UTC (permalink / raw)
  To: libstdc++, gcc-patches

This needs tests, and doesn't include the changes to the standard
containers to add insert_range etc. (but they work with ranges::to
anyway, using the existing member functions).

I plan to write the tests and push this tomorrow.

I've trimmed the boring bits of the version.h changes, that are caused
just by reordering some existing entries to be in alphabetical order.

-- >8 --

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.
---
 libstdc++-v3/include/bits/ranges_base.h |   6 +
 libstdc++-v3/include/bits/version.def   |  34 ++-
 libstdc++-v3/include/bits/version.h     | 111 +++++-----
 libstdc++-v3/include/std/ranges         | 279 +++++++++++++++++++++++-
 4 files changed, 371 insertions(+), 59 deletions(-)

diff --git a/libstdc++-v3/include/bits/ranges_base.h b/libstdc++-v3/include/bits/ranges_base.h
index 7fa43d1965a..555065b4ed7 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)
@@ -1057,6 +1058,11 @@ namespace ranges
 						iterator_t<_Range>,
 						dangling>;
 
+#if __glibcxx_ranges_to_container // C++ >= 23
+  struct from_range_t { explicit from_range_t() = default; };
+  inline constexpr from_range_t from_range{};
+#endif
+
 } // namespace ranges
 _GLIBCXX_END_NAMESPACE_VERSION
 } // namespace std
diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def
index 447fdeb9519..15bd502f52c 100644
--- a/libstdc++-v3/include/bits/version.def
+++ b/libstdc++-v3/include/bits/version.def
@@ -1370,19 +1370,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;
   };
 };
 
@@ -1614,6 +1616,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 97c6d8508f4..9563b6cd2f7 100644
--- a/libstdc++-v3/include/bits/version.h
+++ b/libstdc++-v3/include/bits/version.h
@@ -1658,29 +1658,18 @@
 #endif /* !defined(__cpp_lib_reference_from_temporary) && defined(__glibcxx_want_reference_from_temporary) */
 #undef __glibcxx_want_reference_from_temporary
 
-// from version.def line 1374
-#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 1383
+#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 1382
-#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 1390
+// from version.def line 1392
 #if !defined(__cpp_lib_ranges_zip)
 # if (__cplusplus >= 202100L)
 #  define __glibcxx_ranges_zip 202110L
@@ -1977,7 +1966,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 1618
+// from version.def line 1620
+#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 1628
+#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 1636
 #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..fcc0a786091 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,284 @@ namespace views::__adaptor
 
   namespace views = ranges::views;
 
+#if __cpp_lib_ranges_to_container // C++ >= 23
+namespace ranges
+{
+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) {
+	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 back_inserter(__c);
+      else
+	return inserter(__c, __c.end());
+    }
+
+  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;
+    };
+
+  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>()...);
+    };
+
+  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
+
+  template<typename _Cont, input_range _Rg, typename... _Args>
+    requires (!view<_Cont>)
+    constexpr _Cont
+    to(_Rg&& __r, _Args&&... __args)
+    {
+      static_assert(!is_const_v<_Cont> && !is_volatile_v<_Cont>);
+      static_assert(is_class_v<_Cont>);
+      if constexpr (!input_range<_Cont>
+	  || convertible_to<range_reference_t<_Rg>, range_value_t<_Cont>>)
+	{
+	  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 (common_range<_Rg>
+	      && derived_from<__iter_category_t<iterator_t<_Rg>>,
+			      input_iterator_tag>
+	      && 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>>);
+	  return ranges::to<_Cont>(__r | views::transform( // XXX not in scope
+		[]<typename _Elt>(_Elt&& __elem) {
+		  using _ValT = range_value_t<_Cont>;
+		  return ranges::to<_ValT>(std::forward<_Elt>(__elem));
+		}), std::forward<_Args>(__args)...);
+	}
+    }
+
+  template<template<typename...> typename _Cont, input_range _Rg,
+	   typename... _Args>
+    constexpr auto
+    to(_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.
+    }
+
+  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));
+	}
+    };
+
+  template<typename _Cont, typename... _Args>
+    requires (!view<_Cont>)
+    constexpr _ToClosure<_Cont, _Args...>
+    to(_Args&&... __args)
+    { return {std::forward<_Args>(__args)...}; }
+
+  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));
+	}
+    };
+
+  template<template<typename...> typename _Cont, typename... _Args>
+    constexpr _ToClosure2<_Cont, _Args...>
+    to(_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 */
-- 
2.41.0


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [committed v2] libstdc++: Define std::ranges::to for C++23 (P1206R7) [PR111055]
  2023-11-17 15:49 [PATCH] libstdc++: Define std::ranges::to for C++23 (P1206R7) [PR111055] Jonathan Wakely
@ 2023-11-23 17:51 ` Jonathan Wakely
  2023-11-28  0:02   ` Hans-Peter Nilsson
  2023-11-29 16:28   ` Patrick Palka
  0 siblings, 2 replies; 5+ messages in thread
From: Jonathan Wakely @ 2023-11-23 17:51 UTC (permalink / raw)
  To: libstdc++, gcc-patches

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>
+		  || 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
-- 
2.42.0


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [committed v2] libstdc++: Define std::ranges::to for C++23 (P1206R7) [PR111055]
  2023-11-23 17:51 ` [committed v2] " Jonathan Wakely
@ 2023-11-28  0:02   ` Hans-Peter Nilsson
  2023-11-29 16:28   ` Patrick Palka
  1 sibling, 0 replies; 5+ messages in thread
From: Hans-Peter Nilsson @ 2023-11-28  0:02 UTC (permalink / raw)
  To: Jonathan Wakely; +Cc: libstdc++, gcc-patches

> 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

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [committed v2] libstdc++: Define std::ranges::to for C++23 (P1206R7) [PR111055]
  2023-11-23 17:51 ` [committed v2] " Jonathan Wakely
  2023-11-28  0:02   ` Hans-Peter Nilsson
@ 2023-11-29 16:28   ` Patrick Palka
  2023-11-30 15:49     ` Jonathan Wakely
  1 sibling, 1 reply; 5+ messages in thread
From: Patrick Palka @ 2023-11-29 16:28 UTC (permalink / raw)
  To: Jonathan Wakely; +Cc: libstdc++, gcc-patches

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
> 
> 


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [committed v2] libstdc++: Define std::ranges::to for C++23 (P1206R7) [PR111055]
  2023-11-29 16:28   ` Patrick Palka
@ 2023-11-30 15:49     ` Jonathan Wakely
  0 siblings, 0 replies; 5+ messages in thread
From: Jonathan Wakely @ 2023-11-30 15:49 UTC (permalink / raw)
  To: Patrick Palka; +Cc: libstdc++, gcc-patches

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!


^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2023-11-30 15:49 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-11-17 15:49 [PATCH] libstdc++: Define std::ranges::to for C++23 (P1206R7) [PR111055] Jonathan Wakely
2023-11-23 17:51 ` [committed v2] " Jonathan Wakely
2023-11-28  0:02   ` Hans-Peter Nilsson
2023-11-29 16:28   ` Patrick Palka
2023-11-30 15:49     ` Jonathan Wakely

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).