public inbox for libstdc++@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH] libstdc++: Reimplement range adaptors [PR99433]
@ 2021-03-29  1:58 Patrick Palka
  2021-03-29 14:33 ` Patrick Palka
  0 siblings, 1 reply; 3+ messages in thread
From: Patrick Palka @ 2021-03-29  1:58 UTC (permalink / raw)
  To: gcc-patches; +Cc: libstdc++, Patrick Palka

This rewrites our range adaptor implementation for more comprehensible
error messages, improved SFINAE behavior and conformance to P2281.

The diagnostic improvements mostly comes from using appropriately named
functors instead of lambdas in the generic implementation of partial
application and composition of range adaptors, and in the definition of
each of the standard range adaptors.  This makes their pretty printed
types of much more comprehensible.

The improved SFINAE behavior comes from constraining the range adaptors'
member functions appropriately.  This change fixes PR99433, and was also
necessary in order to implement the wording changes of P2281.

Finally, P2281 clarified that partial application and composition of
range adaptors behaves like a perfect forwarding call wrapper.  This
patch implements this, except that we don't bother adding overloads for
forwarding captured state entities as non-const lvalues, since it seems
we only really need to handle the const lvalue and non-const rvalue
cases.  Such overloads can be easily added if they turn out to be
needed.

Tested on x86_64-pc-linux-gnu, does this look OK for trunk?

libstdc++-v3/ChangeLog:

	PR libstdc++/99433
	* include/std/ranges (views::__adaptor): Remove this namespace,
	moving everything within into the parent namespace.
	(__maybe_refwrap): Remove.
	(_detail::__adaptor_invocable): New concept.
	(_detail::__adaptor_partial_app_viable): New concept.
	(_RangeAdaptor): Rename template parameter _Callable to _Impl.
	Remove the now unnecessary deduction guide.
	(_RangeAdaptor::_M_callable): Remove data member.
	(_RangeAdaptor::_RangeAdaptor): Remove constructors.
	(_RangeAdaptor::operator()): Rewrite
	(_RangeAdaptor::_S_arity): Define.
	(_RangeAdaptor::_Partial): New class template.
	(__detail::__pipe_invocable): New concept.
	(_Pipe): New class template.
	(_RangeAdaptorClosure): Rename template parameter _Callable to
	_Callable to _Impl.  Don't derive from _RangeAdaptor.  New data
	member _M_impl.  Add a defaulted default constructor.  Add an
	explicit constructor taking an _Impl object.  Remove the now
	unnecessary deduction guide.
	(_RangeAdaptorClosure::operator()): Reimplement.
	(_RangeAdaptorClosure::operator|): Reimplement.
	(__detail::__can_ref_view): New concept.
	(__detail::__can_subrange): New concept.
	(all): Replace the lambda here with ...
	(_All): ... this functor.  Add appropriate constraints.
	Remove redundant viewable_range constraint.
	(__detail::__can_filter_view): New concept.
	(filter, _Filter): Like with all/_All.
	(__detail::__can_transform): New concept.
	(transform, _Transform): Like with all/_All.
	(__detail::__can_take_view): New concept.
	(take, _Take): Like with all/_All.
	(__detail::__can_take_while_view): New concept.
	(take_while, _TakeWhile): Like with all/_All.
	(__detail::__can_drop_view): New concept.
	(drop, _Drop): Like with all/_All.
	(__detail::__can_drop_while_view): New concept.
	(drop_while, _DropWhile): Like with all/_All.
	(__detail::__can_join_view): New concept.
	(join, _Join): Like with all/_All.
	(__detail::__can_split_view): New concept.
	(split, _Split): Like with all/_All.  Rename template parameter
	_Fp to _Pattern).
	(__detail::__already_common): New concept.
	(__detail::__can_common_view): New concept.
	(common, _Common): Like with all/_All.
	(__detail::__can_reverse_view): New concept.
	(reverse, _Reverse): Like with all/_All.
	(__detail::__can_elements_view): New concept.
	(elements, _Elements): Like with all/_All.
	(keys, values): Adjust.
	* testsuite/std/ranges/adaptors/99433.cc: New test.
	* testsuite/std/ranges/adaptors/all.cc (test05): New test.
	* testsuite/std/ranges/adaptors/common.cc (test03): New test.
	* testsuite/std/ranges/adaptors/drop.cc (test09): New test.
	* testsuite/std/ranges/adaptors/drop_while.cc (test04): New test.
	* testsuite/std/ranges/adaptors/elements.cc (test04): New test.
	* testsuite/std/ranges/adaptors/filter.cc (test06): New test.
	* testsuite/std/ranges/adaptors/join.cc (test09): New test.
	* testsuite/std/ranges/adaptors/p2281.cc: New test.
	* testsuite/std/ranges/adaptors/reverse.cc (test07): New test.
	* testsuite/std/ranges/adaptors/split.cc (test01, test04):
	Adjust.
	(test09): New test.
	* testsuite/std/ranges/adaptors/split_neg.cc (test01): Adjust
	expected error message.
	(test02): Likewise.  Extend test.
	* testsuite/std/ranges/adaptors/take.cc (test06): New test.
	* testsuite/std/ranges/adaptors/take_while.cc (test05): New test.
	* testsuite/std/ranges/adaptors/transform.cc (test07, test08):
	New test.
---
 libstdc++-v3/include/std/ranges               | 713 ++++++++++++------
 .../testsuite/std/ranges/adaptors/99433.cc    |  41 +
 .../testsuite/std/ranges/adaptors/all.cc      |  12 +
 .../testsuite/std/ranges/adaptors/common.cc   |  12 +
 .../testsuite/std/ranges/adaptors/drop.cc     |  18 +
 .../std/ranges/adaptors/drop_while.cc         |  18 +
 .../testsuite/std/ranges/adaptors/elements.cc |  12 +
 .../testsuite/std/ranges/adaptors/filter.cc   |  18 +
 .../testsuite/std/ranges/adaptors/join.cc     |  12 +
 .../testsuite/std/ranges/adaptors/p2281.cc    |  83 ++
 .../testsuite/std/ranges/adaptors/reverse.cc  |  15 +-
 .../testsuite/std/ranges/adaptors/split.cc    |  35 +-
 .../std/ranges/adaptors/split_neg.cc          |  10 +-
 .../testsuite/std/ranges/adaptors/take.cc     |  18 +
 .../std/ranges/adaptors/take_while.cc         |  18 +
 .../std/ranges/adaptors/transform.cc          |  37 +
 16 files changed, 825 insertions(+), 247 deletions(-)
 create mode 100644 libstdc++-v3/testsuite/std/ranges/adaptors/99433.cc
 create mode 100644 libstdc++-v3/testsuite/std/ranges/adaptors/p2281.cc

diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges
index adbc6d7b274..a65c2a891a5 100644
--- a/libstdc++-v3/include/std/ranges
+++ b/libstdc++-v3/include/std/ranges
@@ -726,165 +726,208 @@ namespace __detail
 
 namespace views
 {
-  namespace __adaptor
+  namespace __detail
   {
-    template<typename _Tp>
-      inline constexpr auto
-      __maybe_refwrap(_Tp& __arg)
-      { return reference_wrapper<_Tp>{__arg}; }
+    template<typename _Impl, typename... _Args>
+      concept __adaptor_invocable
+	= requires { std::declval<_Impl>()(declval<_Args>()...); };
+
+    template<typename _Impl, typename... _Args>
+      concept __adaptor_partial_app_viable = (_Impl::_S_arity > 1)
+	&& (sizeof...(_Args) == _Impl::_S_arity - 1)
+	&& (constructible_from<decay_t<_Args>, _Args> && ...);
+  } // namespace __detail
 
-    template<typename _Tp>
-      inline constexpr auto
-      __maybe_refwrap(const _Tp& __arg)
-      { return reference_wrapper<const _Tp>{__arg}; }
+  template<typename _Impl>
+    struct _RangeAdaptorClosure;
 
-    template<typename _Tp>
-      inline constexpr decltype(auto)
-      __maybe_refwrap(_Tp&& __arg)
-      { return std::forward<_Tp>(__arg); }
+  template<typename _Impl>
+    struct _RangeAdaptor
+    {
+      template<viewable_range _Range, typename... _Args>
+	requires __detail::__adaptor_invocable<_Impl, _Range, _Args...>
+	constexpr auto
+	operator()(_Range&& __r, _Args&&... __args) const
+	{
+	  return _Impl{}(std::forward<_Range>(__r), std::forward<_Args>(__args)...);
+	}
 
-    template<typename _Callable>
-      struct _RangeAdaptorClosure;
+      template<typename... _Args>
+	struct _Partial;
 
-    template<typename _Callable>
-      struct _RangeAdaptor
-      {
-      protected:
-	[[no_unique_address]]
-	  __detail::__maybe_present_t<!is_default_constructible_v<_Callable>,
-				      _Callable> _M_callable;
+      template<typename... _Args>
+	requires __detail::__adaptor_partial_app_viable<_Impl, _Args...>
+	constexpr auto
+	operator()(_Args&&... __args) const
+	{
+	  return _RangeAdaptorClosure{_Partial{std::forward<_Args>(__args)...}};
+	}
 
-      public:
-	constexpr
-	_RangeAdaptor(const _Callable& = {})
-	  requires is_default_constructible_v<_Callable>
-	{ }
+      static_assert(_Impl::_S_arity > 1,
+		    "a range adaptor that accepts only one argument "
+		    "must be defined as a _RangeAdaptorClosure");
+    };
 
-	constexpr
-	_RangeAdaptor(_Callable __callable)
-	  requires (!is_default_constructible_v<_Callable>)
-	  : _M_callable(std::move(__callable))
-	{ }
+  // Represents partial application of _Impl with arguments _Args.
+  template<typename _Impl>
+  template<typename... _Args>
+    struct _RangeAdaptor<_Impl>::_Partial
+    {
+      tuple<_Args...> _M_args;
 
-	template<typename... _Args>
-	  requires (sizeof...(_Args) >= 1)
-	  constexpr auto
-	  operator()(_Args&&... __args) const
-	  {
-	    // [range.adaptor.object]: If a range adaptor object accepts more
-	    // than one argument, then the following expressions are equivalent:
-	    //
-	    //   (1) adaptor(range, args...)
-	    //   (2) adaptor(args...)(range)
-	    //   (3) range | adaptor(args...)
-	    //
-	    // In this case, adaptor(args...) is a range adaptor closure object.
-	    //
-	    // We handle (1) and (2) here, and (3) is just a special case of a
-	    // more general case already handled by _RangeAdaptorClosure.
-	    if constexpr (is_invocable_v<_Callable, _Args...>)
-	      {
-		static_assert(sizeof...(_Args) != 1,
-			      "a _RangeAdaptor that accepts only one argument "
-			      "should be defined as a _RangeAdaptorClosure");
-		// Here we handle adaptor(range, args...) -- just forward all
-		// arguments to the underlying adaptor routine.
-		return _Callable{}(std::forward<_Args>(__args)...);
-	      }
-	    else
-	      {
-		// Here we handle adaptor(args...)(range).
-		// Given args..., we return a _RangeAdaptorClosure that takes a
-		// range argument, such that (2) is equivalent to (1).
-		//
-		// We need to be careful about how we capture args... in this
-		// closure.  By using __maybe_refwrap, we capture lvalue
-		// references by reference (through a reference_wrapper) and
-		// otherwise capture by value.
-		auto __closure
-		  = [...__args(__maybe_refwrap(std::forward<_Args>(__args)))]
-		    <typename _Range> (_Range&& __r) {
-		      // This static_cast has two purposes: it forwards a
-		      // reference_wrapper<T> capture as a T&, and otherwise
-		      // forwards the captured argument as an rvalue.
-		      return _Callable{}(std::forward<_Range>(__r),
-			       (static_cast<unwrap_reference_t
-					    <remove_const_t<decltype(__args)>>>
-				(__args))...);
-		    };
-		using _ClosureType = decltype(__closure);
-		return _RangeAdaptorClosure<_ClosureType>(std::move(__closure));
-	      }
-	  }
-      };
+      constexpr
+      _Partial(_Args... __args)
+	: _M_args(std::move(__args)...)
+      { }
 
-    template<typename _Callable>
-      _RangeAdaptor(_Callable) -> _RangeAdaptor<_Callable>;
+      template<typename _Range>
+	requires __detail::__adaptor_invocable<_Impl, _Range, const _Args&...>
+	constexpr auto
+	operator()(_Range&& __r) const &
+	{
+	  auto __forwarder = [&__r] (const auto&... __args) {
+	    return _Impl{}(std::forward<_Range>(__r), __args...);
+	  };
+	  return std::apply(__forwarder, _M_args);
+	}
 
-    template<typename _Callable>
-      struct _RangeAdaptorClosure : public _RangeAdaptor<_Callable>
-      {
-	using _RangeAdaptor<_Callable>::_RangeAdaptor;
+      template<typename _Range>
+	requires __detail::__adaptor_invocable<_Impl, _Range, _Args...>
+	constexpr auto
+	operator()(_Range&& __r) &&
+	{
+	  auto __forwarder = [&__r] (auto&... __args) {
+	    return _Impl{}(std::forward<_Range>(__r), std::move(__args)...);
+	  };
+	  return std::apply(__forwarder, _M_args);
+	}
 
-	template<viewable_range _Range>
-	  requires requires { declval<_Callable>()(declval<_Range>()); }
-	  constexpr auto
-	  operator()(_Range&& __r) const
-	  {
-	    if constexpr (is_default_constructible_v<_Callable>)
-	      return _Callable{}(std::forward<_Range>(__r));
-	    else
-	      return this->_M_callable(std::forward<_Range>(__r));
-	  }
+      template<typename _Range>
+	constexpr auto
+	operator()(_Range&& __r) const && = delete;
 
-	template<viewable_range _Range>
-	  requires requires { declval<_Callable>()(declval<_Range>()); }
-	  friend constexpr auto
-	  operator|(_Range&& __r, const _RangeAdaptorClosure& __o)
-	  { return __o(std::forward<_Range>(__r)); }
+      static constexpr int _S_arity = 1;
+    };
 
-	template<typename _Tp>
-	  friend constexpr auto
-	  operator|(const _RangeAdaptorClosure<_Tp>& __x,
-		    const _RangeAdaptorClosure& __y)
-	  {
-	    if constexpr (is_default_constructible_v<_Tp>
-			  && is_default_constructible_v<_Callable>)
-	      {
-		auto __closure = [] <typename _Up> (_Up&& __e) {
-		  return std::forward<_Up>(__e) | decltype(__x){} | decltype(__y){};
-		};
-		return _RangeAdaptorClosure<decltype(__closure)>(__closure);
-	      }
-	    else if constexpr (is_default_constructible_v<_Tp>
-			       && !is_default_constructible_v<_Callable>)
-	      {
-		auto __closure = [__y] <typename _Up> (_Up&& __e) {
-		  return std::forward<_Up>(__e) | decltype(__x){} | __y;
-		};
-		return _RangeAdaptorClosure<decltype(__closure)>(__closure);
-	      }
-	    else if constexpr (!is_default_constructible_v<_Tp>
-			       && is_default_constructible_v<_Callable>)
-	      {
-		auto __closure = [__x] <typename _Up> (_Up&& __e) {
-		  return std::forward<_Up>(__e) | __x | decltype(__y){};
-		};
-		return _RangeAdaptorClosure<decltype(__closure)>(__closure);
-	      }
-	    else
-	      {
-		auto __closure = [__x, __y] <typename _Up> (_Up&& __e) {
-		  return std::forward<_Up>(__e) | __x | __y;
-		};
-		return _RangeAdaptorClosure<decltype(__closure)>(__closure);
-	      }
-	  }
-      };
+  // A lightweight partial specialization of the above primary template for
+  // the common case where _Impl accepts a single extra argument.
+  template<typename _Impl>
+  template<typename _Arg>
+    struct _RangeAdaptor<_Impl>::_Partial<_Arg>
+    {
+      _Arg _M_arg;
 
-    template<typename _Callable>
-      _RangeAdaptorClosure(_Callable) -> _RangeAdaptorClosure<_Callable>;
-  } // namespace __adaptor
+      constexpr
+      _Partial(_Arg __arg)
+	: _M_arg(std::move(__arg))
+      { }
+
+      template<typename _Range>
+	requires __detail::__adaptor_invocable<_Impl, _Range, const _Arg&>
+	constexpr auto
+	operator()(_Range&& __r) const &
+	{ return _Impl{}(std::forward<_Range>(__r), _M_arg); }
+
+      template<typename _Range>
+	requires __detail::__adaptor_invocable<_Impl, _Range, _Arg>
+	constexpr auto
+	operator()(_Range&& __r) &&
+	{ return _Impl{}(std::forward<_Range>(__r), std::move(_M_arg)); }
+
+      template<typename _Range>
+	constexpr auto
+	operator()(_Range&& __r) const && = delete;
+
+      static constexpr int _S_arity = 1;
+    };
+
+  namespace __detail
+  {
+    template<typename _Lhs, typename _Rhs, typename _Range>
+      concept __pipe_invocable
+	= requires { std::declval<_Rhs>()(std::declval<_Lhs>()(std::declval<_Range>())); };
+  } // namespace __detail
+
+  // Represents composition of the range adaptors _Lhs and _Rhs.
+  template<typename _Lhs, typename _Rhs>
+    struct _Pipe
+    {
+      [[no_unique_address]] _RangeAdaptorClosure<_Lhs> _M_lhs;
+      [[no_unique_address]] _RangeAdaptorClosure<_Rhs> _M_rhs;
+
+      constexpr
+      _Pipe(_RangeAdaptorClosure<_Lhs> __lhs,
+	    _RangeAdaptorClosure<_Rhs> __rhs)
+	: _M_lhs(std::move(__lhs)), _M_rhs(std::move(__rhs))
+      { }
+
+      template<typename _Range>
+	requires __detail::__pipe_invocable<const _Lhs&, const _Rhs&, _Range>
+	constexpr auto
+	operator()(_Range&& __r) const &
+	{ return _M_rhs(_M_lhs(std::forward<_Range>(__r))); }
+
+      template<typename _Range>
+	requires __detail::__pipe_invocable<_Lhs, _Rhs, _Range>
+	constexpr auto
+	operator()(_Range&& __r) &&
+	{ return std::move(_M_rhs)(std::move(_M_lhs)(std::forward<_Range>(__r))); }
+
+      template<typename _Range>
+	constexpr auto
+	operator()(_Range&& __r) const && = delete;
+
+      static constexpr int _S_arity = 1;
+    };
+
+  template<typename _Impl>
+    struct _RangeAdaptorClosure
+    {
+      [[no_unique_address]] _Impl _M_impl;
+
+      _RangeAdaptorClosure() = default;
+
+      constexpr explicit
+      _RangeAdaptorClosure(_Impl __impl)
+	: _M_impl(std::move(__impl))
+      { }
+
+      template<viewable_range _Range>
+	requires __detail::__adaptor_invocable<const _Impl&, _Range>
+	constexpr auto
+	operator()(_Range&& __r) const &
+	{ return _M_impl(std::forward<_Range>(__r)); }
+
+      template<viewable_range _Range>
+	requires __detail::__adaptor_invocable<_Impl, _Range>
+	constexpr auto
+	operator()(_Range&& __r) &&
+	{ return std::move(_M_impl)(std::forward<_Range>(__r)); }
+
+      template<viewable_range _Range>
+	constexpr auto
+	operator()(_Range&& __r) const && = delete;
+
+      template<typename _Self, typename _Range>
+	requires same_as<remove_cvref_t<_Self>, _RangeAdaptorClosure>
+	  && __detail::__adaptor_invocable<_Self, _Range>
+	friend constexpr auto
+	operator|(_Range&& __r, _Self&& __self)
+	{ return std::forward<_Self>(__self)._M_impl(std::forward<_Range>(__r)); }
+
+      template<typename _Rhs>
+	friend constexpr auto
+	operator|(_RangeAdaptorClosure __lhs, _RangeAdaptorClosure<_Rhs> __rhs)
+	{
+	  using _Lhs = _Impl;
+	  return _RangeAdaptorClosure<_Pipe<_Lhs, _Rhs>>{{std::move(__lhs),
+							  std::move(__rhs)}};
+	}
+
+      static_assert(_Impl::_S_arity == 1,
+		    "a range adaptor that takes extra arguments "
+		    "must be defined as a _RangeAdaptor");
+    };
 } // namespace views
 
   template<range _Range> requires is_object_v<_Range>
@@ -941,20 +984,39 @@ namespace views
 
   namespace views
   {
-    inline constexpr __adaptor::_RangeAdaptorClosure all
-      = [] <viewable_range _Range> (_Range&& __r)
-      {
-	if constexpr (view<decay_t<_Range>>)
-	  return std::forward<_Range>(__r);
-	else if constexpr (requires { ref_view{std::forward<_Range>(__r)}; })
-	  return ref_view{std::forward<_Range>(__r)};
-	else
-	  return subrange{std::forward<_Range>(__r)};
-      };
+    namespace __detail
+    {
+      template<typename _Range>
+	concept __can_ref_view = requires { ref_view{std::declval<_Range>()}; };
+
+      template<typename _Range>
+	concept __can_subrange = requires { subrange{std::declval<_Range>()}; };
+    } // namespace __detail
+
+    struct _All
+    {
+      template<typename _Range>
+	requires view<decay_t<_Range>>
+	  || __detail::__can_ref_view<_Range>
+	  || __detail::__can_subrange<_Range>
+	constexpr auto
+	operator()(_Range&& __r) const
+	{
+	  if constexpr (view<decay_t<_Range>>)
+	    return std::forward<_Range>(__r);
+	  else if constexpr (__detail::__can_ref_view<_Range>)
+	    return ref_view{std::forward<_Range>(__r)};
+	  else
+	    return subrange{std::forward<_Range>(__r)};
+	}
+
+      static constexpr int _S_arity = 1;
+    };
+
+    inline constexpr _RangeAdaptorClosure<_All> all;
 
     template<viewable_range _Range>
       using all_t = decltype(all(std::declval<_Range>()));
-
   } // namespace views
 
   // XXX: the following algos are copied from ranges_algo.h to avoid a circular
@@ -1305,11 +1367,27 @@ namespace views
 
   namespace views
   {
-    inline constexpr __adaptor::_RangeAdaptor filter
-      = [] <viewable_range _Range, typename _Pred> (_Range&& __r, _Pred&& __p)
-      {
-	return filter_view{std::forward<_Range>(__r), std::forward<_Pred>(__p)};
-      };
+    namespace __detail
+    {
+      template<typename _Range, typename _Pred>
+	concept __can_filter_view
+	  = requires { filter_view{std::declval<_Range>(), std::declval<_Pred>()}; };
+    } // namespace __detail
+
+    struct _Filter
+    {
+      template<typename _Range, typename _Pred>
+	requires __detail::__can_filter_view<_Range, _Pred>
+	constexpr auto
+	operator()(_Range&& __r, _Pred&& __p) const
+	{
+	  return filter_view{std::forward<_Range>(__r), std::forward<_Pred>(__p)};
+	}
+
+      static constexpr int _S_arity = 2;
+    };
+
+    inline constexpr _RangeAdaptor<_Filter> filter;
   } // namespace views
 
   template<input_range _Vp, copy_constructible _Fp>
@@ -1653,11 +1731,27 @@ namespace views
 
   namespace views
   {
-    inline constexpr __adaptor::_RangeAdaptor transform
-      = [] <viewable_range _Range, typename _Fp> (_Range&& __r, _Fp&& __f)
-      {
-	return transform_view{std::forward<_Range>(__r), std::forward<_Fp>(__f)};
-      };
+    namespace __detail
+    {
+      template<typename _Range, typename _Fp>
+	concept __can_transform_view
+	  = requires { transform_view{std::declval<_Range>(), std::declval<_Fp>()}; };
+    } // namespace __detail
+
+    struct _Transform
+    {
+      template<typename _Range, typename _Fp>
+	requires __detail::__can_transform_view<_Range, _Fp>
+	constexpr auto
+	operator()(_Range&& __r, _Fp&& __f) const
+	{
+	  return transform_view{std::forward<_Range>(__r), std::forward<_Fp>(__f)};
+	}
+
+      static constexpr int _S_arity = 2;
+    };
+
+    inline constexpr _RangeAdaptor<_Transform> transform;
   } // namespace views
 
   template<view _Vp>
@@ -1816,11 +1910,27 @@ namespace views
 
   namespace views
   {
-    inline constexpr __adaptor::_RangeAdaptor take
-      = [] <viewable_range _Range, typename _Tp> (_Range&& __r, _Tp&& __n)
-      {
-	return take_view{std::forward<_Range>(__r), std::forward<_Tp>(__n)};
-      };
+    namespace __detail
+    {
+      template<typename _Range, typename _Tp>
+	concept __can_take_view
+	  = requires { take_view{std::declval<_Range>(), std::declval<_Tp>()}; };
+    } // namespace __detail
+
+    struct _Take
+    {
+      template<typename _Range, typename _Tp>
+	requires __detail::__can_take_view<_Range, _Tp>
+	constexpr auto
+	operator()(_Range&& __r, _Tp&& __n) const
+	{
+	  return take_view{std::forward<_Range>(__r), std::forward<_Tp>(__n)};
+	}
+
+      static constexpr int _S_arity = 2;
+    };
+
+    inline constexpr _RangeAdaptor<_Take> take;
   } // namespace views
 
   template<view _Vp, typename _Pred>
@@ -1918,11 +2028,27 @@ namespace views
 
   namespace views
   {
-    inline constexpr __adaptor::_RangeAdaptor take_while
-      = [] <viewable_range _Range, typename _Pred> (_Range&& __r, _Pred&& __p)
-      {
-	return take_while_view{std::forward<_Range>(__r), std::forward<_Pred>(__p)};
-      };
+    namespace __detail
+    {
+      template<typename _Range, typename _Pred>
+	concept __can_take_while_view
+	  = requires { take_while_view{std::declval<_Range>(), std::declval<_Pred>()}; };
+    } // namespace __detail
+
+    struct _TakeWhile
+    {
+      template<typename _Range, typename _Pred>
+	requires __detail::__can_take_while_view<_Range, _Pred>
+	constexpr auto
+	operator()(_Range&& __r, _Pred&& __p) const
+	{
+	  return take_while_view{std::forward<_Range>(__r), std::forward<_Pred>(__p)};
+	}
+
+      static constexpr int _S_arity = 2;
+    };
+
+    inline constexpr _RangeAdaptor<_TakeWhile> take_while;
   } // namespace views
 
   template<view _Vp>
@@ -2020,11 +2146,27 @@ namespace views
 
   namespace views
   {
-    inline constexpr __adaptor::_RangeAdaptor drop
-      = [] <viewable_range _Range, typename _Tp> (_Range&& __r, _Tp&& __n)
-      {
-	return drop_view{std::forward<_Range>(__r), std::forward<_Tp>(__n)};
-      };
+    namespace __detail
+    {
+      template<typename _Range, typename _Tp>
+	concept __can_drop_view
+	  = requires { drop_view{std::declval<_Range>(), std::declval<_Tp>()}; };
+    } // namespace __detail
+
+    struct _Drop
+    {
+      template<typename _Range, typename _Tp>
+	requires __detail::__can_drop_view<_Range, _Tp>
+	constexpr auto
+	operator()(_Range&& __r, _Tp&& __n) const
+	{
+	  return drop_view{std::forward<_Range>(__r), std::forward<_Tp>(__n)};
+	}
+
+      static constexpr int _S_arity = 2;
+    };
+
+    inline constexpr _RangeAdaptor<_Drop> drop;
   } // namespace views
 
   template<view _Vp, typename _Pred>
@@ -2085,12 +2227,28 @@ namespace views
 
   namespace views
   {
-    inline constexpr __adaptor::_RangeAdaptor drop_while
-      = [] <viewable_range _Range, typename _Pred> (_Range&& __r, _Pred&& __p)
-      {
-	return drop_while_view{std::forward<_Range>(__r),
-			       std::forward<_Pred>(__p)};
-      };
+    namespace __detail
+    {
+      template<typename _Range, typename _Pred>
+	concept __can_drop_while_view
+	  = requires { drop_while_view{std::declval<_Range>(), std::declval<_Pred>()}; };
+    } // namespace __detail
+
+    struct _DropWhile
+    {
+      template<typename _Range, typename _Pred>
+	requires __detail::__can_drop_while_view<_Range, _Pred>
+	constexpr auto
+	operator()(_Range&& __r, _Pred&& __p) const
+	{
+	  return drop_while_view{std::forward<_Range>(__r),
+				 std::forward<_Pred>(__p)};
+	}
+
+      static constexpr int _S_arity = 2;
+    };
+
+    inline constexpr _RangeAdaptor<_DropWhile> drop_while;
   } // namespace views
 
   template<input_range _Vp>
@@ -2409,13 +2567,29 @@ namespace views
 
   namespace views
   {
-    inline constexpr __adaptor::_RangeAdaptorClosure join
-      = [] <viewable_range _Range> (_Range&& __r)
-      {
-	// _GLIBCXX_RESOLVE_LIB_DEFECTS
-	// 3474. Nesting join_views is broken because of CTAD
-	return join_view<views::all_t<_Range>>{std::forward<_Range>(__r)};
-      };
+    namespace __detail
+    {
+      template<typename _Range>
+	concept __can_join_view
+	  = requires { join_view<all_t<_Range>>{std::declval<_Range>()}; };
+    } // namespace __detail
+
+    struct _Join
+    {
+      template<typename _Range>
+	requires __detail::__can_join_view<_Range>
+	constexpr auto
+	operator()(_Range&& __r) const
+	{
+	  // _GLIBCXX_RESOLVE_LIB_DEFECTS
+	  // 3474. Nesting join_views is broken because of CTAD
+	  return join_view<all_t<_Range>>{std::forward<_Range>(__r)};
+	}
+
+      static constexpr int _S_arity = 1;
+    };
+
+    inline constexpr _RangeAdaptorClosure<_Join> join;
   } // namespace views
 
   namespace __detail
@@ -2784,9 +2958,9 @@ namespace views
       }
     };
 
-  template<typename _Range, typename _Pred>
-    split_view(_Range&&, _Pred&&)
-      -> split_view<views::all_t<_Range>, views::all_t<_Pred>>;
+  template<typename _Range, typename _Pattern>
+    split_view(_Range&&, _Pattern&&)
+      -> split_view<views::all_t<_Range>, views::all_t<_Pattern>>;
 
   template<input_range _Range>
     split_view(_Range&&, range_value_t<_Range>)
@@ -2794,11 +2968,27 @@ namespace views
 
   namespace views
   {
-    inline constexpr __adaptor::_RangeAdaptor split
-      = [] <viewable_range _Range, typename _Fp> (_Range&& __r, _Fp&& __f)
-      {
-	return split_view{std::forward<_Range>(__r), std::forward<_Fp>(__f)};
-      };
+    namespace __detail
+    {
+      template<typename _Range, typename _Pattern>
+	concept __can_split_view
+	  = requires { split_view{std::declval<_Range>(), std::declval<_Pattern>()}; };
+    } // namespace __detail
+
+    struct _Split
+    {
+      template<typename _Range, typename _Pattern>
+	requires __detail::__can_split_view<_Range, _Pattern>
+	constexpr auto
+	operator()(_Range&& __r, _Pattern&& __f) const
+	{
+	  return split_view{std::forward<_Range>(__r), std::forward<_Pattern>(__f)};
+	}
+
+      static constexpr int _S_arity = 2;
+    };
+
+    inline constexpr _RangeAdaptor<_Split> split;
   } // namespace views
 
   namespace views
@@ -2911,16 +3101,35 @@ namespace views
 
   namespace views
   {
-    inline constexpr __adaptor::_RangeAdaptorClosure common
-      = [] <viewable_range _Range> (_Range&& __r)
-      {
-	if constexpr (common_range<_Range>
-		      && requires { views::all(std::forward<_Range>(__r)); })
-	  return views::all(std::forward<_Range>(__r));
-	else
-	  return common_view{std::forward<_Range>(__r)};
-      };
+    namespace __detail
+    {
+      template<typename _Range>
+	concept __already_common = common_range<_Range>
+	  && requires { views::all(std::declval<_Range>()); };
+
+      template<typename _Range>
+	concept __can_common_view
+	  = requires { common_view{std::declval<_Range>()}; };
+    } // namespace __detail
 
+    struct _Common
+    {
+      template<typename _Range>
+	requires __detail::__already_common<_Range>
+	  || __detail::__can_common_view<_Range>
+	constexpr auto
+	operator()(_Range&& __r) const
+	{
+	  if constexpr (__detail::__already_common<_Range>)
+	    return views::all(std::forward<_Range>(__r));
+	  else
+	    return common_view{std::forward<_Range>(__r)};
+	}
+
+      static constexpr int _S_arity = 1;
+    };
+
+    inline constexpr _RangeAdaptorClosure<_Common> common;
   } // namespace views
 
   template<view _Vp>
@@ -3016,27 +3225,42 @@ namespace views
 
       template<typename _Vp>
 	inline constexpr bool __is_reverse_view<reverse_view<_Vp>> = true;
-    }
 
-    inline constexpr __adaptor::_RangeAdaptorClosure reverse
-      = [] <viewable_range _Range> (_Range&& __r)
-      {
-	using _Tp = remove_cvref_t<_Range>;
-	if constexpr (__detail::__is_reverse_view<_Tp>)
-	  return std::forward<_Range>(__r).base();
-	else if constexpr (__detail::__is_reversible_subrange<_Tp>)
-	  {
-	    using _Iter = decltype(ranges::begin(__r).base());
-	    if constexpr (sized_range<_Tp>)
-	      return subrange<_Iter, _Iter, subrange_kind::sized>
-		      (__r.end().base(), __r.begin().base(), __r.size());
-	    else
-	      return subrange<_Iter, _Iter, subrange_kind::unsized>
-		      (__r.end().base(), __r.begin().base());
-	  }
-	else
-	  return reverse_view{std::forward<_Range>(__r)};
-      };
+      template<typename _Range>
+	concept __can_reverse_view
+	  = requires { reverse_view{std::declval<_Range>()}; };
+    } // namespace __detail
+
+    struct _Reverse
+    {
+      template<typename _Range>
+	requires __detail::__is_reverse_view<remove_cvref_t<_Range>>
+	  || __detail::__is_reversible_subrange<remove_cvref_t<_Range>>
+	  || __detail::__can_reverse_view<_Range>
+	constexpr auto
+	operator()(_Range&& __r) const
+	{
+	  using _Tp = remove_cvref_t<_Range>;
+	  if constexpr (__detail::__is_reverse_view<_Tp>)
+	    return std::forward<_Range>(__r).base();
+	  else if constexpr (__detail::__is_reversible_subrange<_Tp>)
+	    {
+	      using _Iter = decltype(ranges::begin(__r).base());
+	      if constexpr (sized_range<_Tp>)
+		return subrange<_Iter, _Iter, subrange_kind::sized>
+			{__r.end().base(), __r.begin().base(), __r.size()};
+	      else
+		return subrange<_Iter, _Iter, subrange_kind::unsized>
+			{__r.end().base(), __r.begin().base()};
+	    }
+	  else
+	    return reverse_view{std::forward<_Range>(__r)};
+	}
+
+      static constexpr int _S_arity = 1;
+    };
+
+    inline constexpr _RangeAdaptorClosure<_Reverse> reverse;
   } // namespace views
 
   namespace __detail
@@ -3335,16 +3559,31 @@ namespace views
 
   namespace views
   {
+    namespace __detail
+    {
+      template<size_t _Nm, typename _Range>
+	concept __can_elements_view
+	  = requires { elements_view<all_t<_Range>, _Nm>{std::declval<_Range>()}; };
+    } // namespace __detail
+
     template<size_t _Nm>
-    inline constexpr __adaptor::_RangeAdaptorClosure elements
-      = [] <viewable_range _Range> (_Range&& __r)
+      struct _Elements
       {
-	using _El = elements_view<views::all_t<_Range>, _Nm>;
-	return _El{std::forward<_Range>(__r)};
+	template<typename _Range>
+	  requires __detail::__can_elements_view<_Nm, _Range>
+	  constexpr auto
+	  operator()(_Range&& __r) const
+	  {
+	    return elements_view<all_t<_Range>, _Nm>{std::forward<_Range>(__r)};
+	  }
+
+	static constexpr int _S_arity = 1;
       };
 
-    inline constexpr __adaptor::_RangeAdaptorClosure keys = elements<0>;
-    inline constexpr __adaptor::_RangeAdaptorClosure values = elements<1>;
+    template<size_t _Nm>
+      inline constexpr _RangeAdaptorClosure<_Elements<_Nm>> elements;
+    inline constexpr auto keys = elements<0>;
+    inline constexpr auto values = elements<1>;
   } // namespace views
 
 } // namespace ranges
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/99433.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/99433.cc
new file mode 100644
index 00000000000..1fd22dfcdf2
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/99433.cc
@@ -0,0 +1,41 @@
+// Copyright (C) 2021 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+// PR libstdc++/99433
+
+#include <ranges>
+#include <vector>
+
+template <typename underlying_adaptor_t>
+struct deep
+{
+  underlying_adaptor_t adaptor;
+
+  template <typename range_t>
+  friend auto operator|(range_t &range, deep const &me)
+  {
+   return me.adaptor(range[0]);
+  }
+};
+
+auto f = [] (auto nucl) -> decltype(nucl + ' ') { return nucl + ' '; };
+auto complement = deep{std::views::transform(f)};
+std::vector<std::vector<char>> foo{};
+auto v = foo | complement;
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/all.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/all.cc
index 33e4b96686e..ee5670d5fd6 100644
--- a/libstdc++-v3/testsuite/std/ranges/adaptors/all.cc
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/all.cc
@@ -112,6 +112,17 @@ static_assert(sizeof(decltype(views::take(5) | views::drop(5)))
 				 | views::drop(5)
 				 | views::reverse)));
 
+template<auto all = views::all>
+void
+test05()
+{
+  // Verify SFINAE behavior.
+  static_assert(!requires { all(); });
+  static_assert(!requires { all(0, 0); });
+  static_assert(!requires { all(0); });
+  static_assert(!requires { 0 | all; });
+}
+
 int
 main()
 {
@@ -119,4 +130,5 @@ main()
   test02();
   static_assert(test03());
   static_assert(test04());
+  test05();
 }
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/common.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/common.cc
index 719b25be006..085e8ff907d 100644
--- a/libstdc++-v3/testsuite/std/ranges/adaptors/common.cc
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/common.cc
@@ -60,9 +60,21 @@ test02()
   VERIFY( std::count(v2.begin(), v2.end(), 1) == 2);
 }
 
+template<auto common = views::common>
+void
+test03()
+{
+  // Verify SFINAE behavior.
+  static_assert(!requires { common(); });
+  static_assert(!requires { common(0, 0); });
+  static_assert(!requires { common(0); });
+  static_assert(!requires { 0 | common; });
+}
+
 int
 main()
 {
   test01();
   test02();
+  test03();
 }
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/drop.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/drop.cc
index 19afba8a9ae..c0525109bd1 100644
--- a/libstdc++-v3/testsuite/std/ranges/adaptors/drop.cc
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/drop.cc
@@ -258,6 +258,23 @@ test08()
   VERIFY( ra_test_wrapper<long>::increment_count == 0 );
 }
 
+template<auto drop = views::drop>
+void
+test09()
+{
+  // Verify SFINAE behavior.
+  extern int x[5];
+  int* n = 0;
+  static_assert(!requires { drop(); });
+  static_assert(!requires { drop(x, n, n); });
+  static_assert(!requires { drop(x, n); });
+  static_assert(!requires { drop(n)(x); });
+  static_assert(!requires { x | (drop(n) | views::all); });
+  static_assert(!requires { (drop(n) | views::all)(x); });
+  static_assert(!requires { drop | views::all; });
+  static_assert(!requires { views::all | drop; });
+}
+
 int
 main()
 {
@@ -269,4 +286,5 @@ main()
   test06();
   test07();
   test08();
+  test09();
 }
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/drop_while.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/drop_while.cc
index e807388538b..58489d54f89 100644
--- a/libstdc++-v3/testsuite/std/ranges/adaptors/drop_while.cc
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/drop_while.cc
@@ -89,6 +89,23 @@ test03()
   VERIFY( ranges::equal(v, (int[]){3,4,5}) );
 }
 
+template<auto drop_while = views::drop_while>
+void
+test04()
+{
+  // Verify SFINAE behavior.
+  extern int x[5];
+  auto p = [] (int*) { return true; };
+  static_assert(!requires { drop_while(); });
+  static_assert(!requires { drop_while(x, p, p); });
+  static_assert(!requires { drop_while(x, p); });
+  static_assert(!requires { drop_while(p)(x); });
+  static_assert(!requires { x | (drop_while(p) | views::all); });
+  static_assert(!requires { (drop_while(p) | views::all)(x); });
+  static_assert(!requires { drop_while | views::all; });
+  static_assert(!requires { views::all | drop_while; });
+}
+
 int
 main()
 {
@@ -96,4 +113,5 @@ main()
   test02();
   test03<forward_iterator_wrapper>();
   test03<random_access_iterator_wrapper>();
+  test04();
 }
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/elements.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/elements.cc
index 76807772006..b0d122f8db5 100644
--- a/libstdc++-v3/testsuite/std/ranges/adaptors/elements.cc
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/elements.cc
@@ -89,10 +89,22 @@ test03()
   VERIFY( (next(b_const, 2) - b_const) == 2 );
 }
 
+template<auto elements = views::elements<0>>
+void
+test04()
+{
+  // Verify SFINAE behavior.
+  static_assert(!requires { elements(); });
+  static_assert(!requires { elements(0, 0); });
+  static_assert(!requires { elements(0); });
+  static_assert(!requires { 0 | elements; });
+}
+
 int
 main()
 {
   test01();
   test02();
   test03();
+  test04();
 }
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/filter.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/filter.cc
index c60eec51368..e0f6b8d4c4b 100644
--- a/libstdc++-v3/testsuite/std/ranges/adaptors/filter.cc
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/filter.cc
@@ -123,6 +123,23 @@ test05()
   VERIFY( ranges::equal(v, (int[]){2,4}) );
 }
 
+template<auto filter = views::filter>
+void
+test06()
+{
+  // Verify SFINAE behavior.
+  extern int x[5];
+  auto p = [] (int*) { return true; };
+  static_assert(!requires { filter(); });
+  static_assert(!requires { filter(x, p, p); });
+  static_assert(!requires { filter(x, p); });
+  static_assert(!requires { filter(p)(x); });
+  static_assert(!requires { x | (filter(p) | views::all); });
+  static_assert(!requires { (filter(p) | views::all)(x); });
+  static_assert(!requires { filter | views::all; });
+  static_assert(!requires { views::all | filter; });
+}
+
 int
 main()
 {
@@ -132,4 +149,5 @@ main()
   test04();
   test05<forward_iterator_wrapper>();
   test05<random_access_iterator_wrapper>();
+  test06();
 }
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc
index 6d780f4887b..fb06a7698af 100644
--- a/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc
@@ -149,6 +149,17 @@ test08()
   VERIFY( i->a == 5 );
 }
 
+template<auto join = views::join>
+void
+test09()
+{
+  // Verify SFINAE behavior.
+  static_assert(!requires { join(); });
+  static_assert(!requires { join(0, 0); });
+  static_assert(!requires { join(0); });
+  static_assert(!requires { 0 | join; });
+}
+
 int
 main()
 {
@@ -160,4 +171,5 @@ main()
   test06();
   test07();
   test08();
+  test09();
 }
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/p2281.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/p2281.cc
new file mode 100644
index 00000000000..c916a5ea8b7
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/p2281.cc
@@ -0,0 +1,83 @@
+// Copyright (C) 2021 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <ranges>
+#include <string>
+#include <string_view>
+#include <testsuite_hooks.h>
+
+namespace ranges = std::ranges;
+namespace views = std::ranges::views;
+
+// Verify P2281 changes to the forwarding semantics of partial application
+// and composition of range adaptor objects.
+
+void
+test01()
+{
+  auto split_into_strings = [] (auto p) {
+    return views::split(p) | views::transform([](auto r){
+      return std::string(r.begin(), ranges::next(r.begin(), r.end()));
+    });
+  };
+  constexpr std::string_view s = "hello world";
+  constexpr std::string_view p = " ";
+  constexpr auto v1 = s | split_into_strings(p);
+  constexpr auto v2 = split_into_strings(p)(s);
+  VERIFY( ranges::equal(v1, (std::string_view[]){"hello", "world"}) );
+  VERIFY( ranges::equal(v2, (std::string_view[]){"hello", "world"}) );
+}
+
+struct move_only_range
+{
+  move_only_range() { }
+  move_only_range(move_only_range&&);
+  move_only_range& operator=(move_only_range&&);
+  move_only_range(const move_only_range&) = delete;
+  move_only_range& operator=(const move_only_range&) = delete;
+  char* begin();
+  char* end();
+};
+
+template<>
+  inline constexpr bool std::ranges::enable_view<move_only_range> = true;
+
+template<auto split = views::split>
+void
+test02()
+{
+  std::string_view s;
+  move_only_range p;
+  static_assert(requires { s | split(std::move(p)); });
+  static_assert(requires { split(std::move(p))(s); });
+  static_assert(requires { split(std::move(p)) | views::all; });
+  static_assert(requires { views::all | split(std::move(p)); });
+  static_assert(!requires { split(p); });
+  static_assert(!requires { split(p) | views::all; });
+  static_assert(!requires { views::all | split(p); });
+}
+
+int
+main()
+{
+  test01();
+  test02();
+}
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/reverse.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/reverse.cc
index 0d52498e207..47e34eb6581 100644
--- a/libstdc++-v3/testsuite/std/ranges/adaptors/reverse.cc
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/reverse.cc
@@ -138,7 +138,8 @@ namespace test_ns
   void make_reverse_iterator(T&&) {}
 } // namespace test_ns
 
-void test06()
+void
+test06()
 {
   // Check that views::reverse works and does not use ADL which could lead
   // to accidentally finding test_ns::make_reverse_iterator(A*).
@@ -149,6 +150,17 @@ void test06()
   static_assert( std::ranges::range<const V> );
 }
 
+template<auto reverse = views::reverse>
+void
+test07()
+{
+  // Verify SFINAE behavior.
+  static_assert(!requires { reverse(); });
+  static_assert(!requires { reverse(0, 0); });
+  static_assert(!requires { reverse(0); });
+  static_assert(!requires { 0 | reverse; });
+}
+
 int
 main()
 {
@@ -158,4 +170,5 @@ main()
   test04();
   test05();
   test06();
+  test07();
 }
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/split.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/split.cc
index dc7f55a2f92..b9fb3728708 100644
--- a/libstdc++-v3/testsuite/std/ranges/adaptors/split.cc
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/split.cc
@@ -39,7 +39,7 @@ test01()
 {
   auto x = "the  quick  brown  fox"sv;
   auto p = std::string{"  "};
-  auto v = x | views::split(p);
+  auto v = x | views::split(views::all(p)); // views::all is needed here after P2281.
   auto i = v.begin();
   VERIFY( ranges::equal(*i++, "the"sv) );
   VERIFY( ranges::equal(*i++, "quick"sv) );
@@ -83,7 +83,7 @@ test04()
   static_assert(!ranges::view<decltype(p)>);
   static_assert(std::same_as<decltype(p | views::all),
 			     ranges::ref_view<decltype(p)>>);
-  auto v = x | views::split(p);
+  auto v = x | views::split(views::all(p)); // views::all is needed here after P2281.
   auto i = v.begin();
   VERIFY( ranges::equal(*i++, "the"sv) );
   VERIFY( ranges::equal(*i++, "quick"sv) );
@@ -152,6 +152,36 @@ test08()
   VERIFY( i == v.end() );
 }
 
+template<auto split = views::split>
+void
+test09()
+{
+  // Verify SFINAE behavior.
+  std::string s, p;
+  static_assert(!requires { split(); });
+  static_assert(!requires { split(s, p, 0); });
+  static_assert(!requires { split(p)(); });
+  static_assert(!requires { s | split; });
+
+  static_assert(!requires { s | split(p); });
+  static_assert(!requires { split(p)(s); });
+  static_assert(!requires { s | (split(p) | views::all); });
+  static_assert(!requires { (split(p) | views::all)(s); });
+
+  static_assert(requires { s | split(views::all(p)); });
+  static_assert(requires { split(views::all(p))(s); });
+  static_assert(requires { s | (split(views::all(p)) | views::all); });
+  static_assert(requires { (split(views::all(p)) | views::all)(s); });
+
+  auto adapt = split(p);
+  static_assert(requires { s | adapt; });
+  static_assert(requires { adapt(s); });
+
+  auto adapt2 = split(p) | views::all;
+  static_assert(requires { s | adapt2; });
+  static_assert(requires { adapt2(s); });
+}
+
 int
 main()
 {
@@ -163,4 +193,5 @@ main()
   test06();
   test07();
   test08();
+  test09();
 }
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/split_neg.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/split_neg.cc
index fb84049711f..4229314a9dc 100644
--- a/libstdc++-v3/testsuite/std/ranges/adaptors/split_neg.cc
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/split_neg.cc
@@ -30,8 +30,7 @@ test01()
 {
   using namespace std::literals;
   auto x = "the  quick  brown  fox"sv;
-  auto v = views::split(x, std::initializer_list<char>{' ', ' '});
-  v.begin(); // { dg-error "" }
+  auto v = views::split(x, std::initializer_list<char>{' ', ' '}); // { dg-error "no match" }
 }
 
 void
@@ -39,11 +38,8 @@ test02()
 {
   using namespace std::literals;
   auto x = "the  quick  brown  fox"sv;
-  auto v = x | views::split(std::initializer_list<char>{' ', ' '}); // { dg-error "no match" }
-  v.begin();
+  auto v1 = views::split(std::initializer_list<char>{' ', ' '})(x); // { dg-error "deleted" }
+  auto v2 = x | views::split(std::initializer_list<char>{' ', ' '}); // { dg-error "no match" }
 }
 
 // { dg-prune-output "in requirements" }
-// { dg-error "deduction failed" "" { target *-*-* } 0 }
-// { dg-error "no match" "" { target *-*-* } 0 }
-// { dg-error "constraint failure" "" { target *-*-* } 0 }
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/take.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/take.cc
index 5947c7bafab..55f74824737 100644
--- a/libstdc++-v3/testsuite/std/ranges/adaptors/take.cc
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/take.cc
@@ -100,6 +100,23 @@ test05()
   b = ranges::end(v);
 }
 
+template<auto take = views::take>
+void
+test06()
+{
+  // Verify SFINAE behavior.
+  extern int x[5];
+  int* n = 0;
+  static_assert(!requires { take(); });
+  static_assert(!requires { take(x, n, n); });
+  static_assert(!requires { take(x, n); });
+  static_assert(!requires { take(n)(x); });
+  static_assert(!requires { x | (take(n) | views::all); });
+  static_assert(!requires { (take(n) | views::all)(x); });
+  static_assert(!requires { take | views::all; });
+  static_assert(!requires { views::all | take; });
+}
+
 int
 main()
 {
@@ -108,4 +125,5 @@ main()
   test03();
   test04();
   test05();
+  test06();
 }
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/take_while.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/take_while.cc
index ff377e578c4..1ab8df3bc62 100644
--- a/libstdc++-v3/testsuite/std/ranges/adaptors/take_while.cc
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/take_while.cc
@@ -79,6 +79,23 @@ test04()
   static_assert(!ranges::range<decltype(v) const>);
 }
 
+template<auto take_while = views::take_while>
+void
+test05()
+{
+  // Verify SFINAE behavior.
+  extern int x[5];
+  auto p = [] (int*) { return true; };
+  static_assert(!requires { take_while(); });
+  static_assert(!requires { take_while(x, p, p); });
+  static_assert(!requires { take_while(x, p); });
+  static_assert(!requires { take_while(p)(x); });
+  static_assert(!requires { x | (take_while(p) | views::all); });
+  static_assert(!requires { (take_while(p) | views::all)(x); });
+  static_assert(!requires { take_while | views::all; });
+  static_assert(!requires { views::all | take_while; });
+}
+
 int
 main()
 {
@@ -86,4 +103,5 @@ main()
   test02();
   test03();
   test04();
+  test05();
 }
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc
index 4e7e7ba0a67..aefe7d4d9a3 100644
--- a/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc
@@ -145,6 +145,41 @@ test06()
   VERIFY( (next(b_const, 2) - b_const) == 2 );
 }
 
+void
+test07()
+{
+  int x[] = {1,2,3,4,5};
+  auto v1 = views::transform([] (auto& x) { return &x; });
+  auto v2 = views::transform([] (auto x) { return *x; });
+  auto v = x | (v1 | v2);
+  // If ADL for the outermost operator| above found the hidden friend function
+  // decltype(v2)::operator|(_Range&&, const decltype(v2)&) then we'd first have
+  // to check this friend function's constraints (because of CWG2369) which
+  // means checking that the call v2(x) is well-formed, which would lead to a
+  // fatal error during return type deduction of the second generic lambda above
+  // (with x having type 'char').  So this test essentially verifies that our
+  // range adaptors are implemented such that ADL doesn't find this unviable
+  // hidden friend function in the first place.
+  VERIFY( ranges::equal(v, x) );
+}
+
+template<auto transform = views::transform>
+void
+test08()
+{
+  // Verify SFINAE behavior.
+  extern int x[5];
+  auto f = [] (int* e) { return e; };
+  static_assert(!requires { transform(); });
+  static_assert(!requires { transform(x, f, f); });
+  static_assert(!requires { transform(x, f); });
+  static_assert(!requires { transform(f)(x); });
+  static_assert(!requires { x | (transform(f) | views::all); });
+  static_assert(!requires { (transform(f) | views::all)(x); });
+  static_assert(!requires { transform | views::all; });
+  static_assert(!requires { views::all | transform; });
+}
+
 int
 main()
 {
@@ -154,4 +189,6 @@ main()
   test04();
   test05();
   test06();
+  test07();
+  test08();
 }
-- 
2.31.1.133.g84d06cdc06


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

* Re: [PATCH] libstdc++: Reimplement range adaptors [PR99433]
  2021-03-29  1:58 [PATCH] libstdc++: Reimplement range adaptors [PR99433] Patrick Palka
@ 2021-03-29 14:33 ` Patrick Palka
  2021-04-08 13:18   ` Jonathan Wakely
  0 siblings, 1 reply; 3+ messages in thread
From: Patrick Palka @ 2021-03-29 14:33 UTC (permalink / raw)
  To: Patrick Palka; +Cc: gcc-patches, libstdc++

On Sun, 28 Mar 2021, Patrick Palka wrote:

> This rewrites our range adaptor implementation for more comprehensible
> error messages, improved SFINAE behavior and conformance to P2281.
> 
> The diagnostic improvements mostly comes from using appropriately named
> functors instead of lambdas in the generic implementation of partial
> application and composition of range adaptors, and in the definition of
> each of the standard range adaptors.  This makes their pretty printed
> types of much more comprehensible.
> 
> The improved SFINAE behavior comes from constraining the range adaptors'
> member functions appropriately.  This change fixes PR99433, and was also
> necessary in order to implement the wording changes of P2281.
> 
> Finally, P2281 clarified that partial application and composition of
> range adaptors behaves like a perfect forwarding call wrapper.  This
> patch implements this, except that we don't bother adding overloads for
> forwarding captured state entities as non-const lvalues, since it seems
> we only really need to handle the const lvalue and non-const rvalue
> cases.  Such overloads can be easily added if they turn out to be
> needed.
> 
> Tested on x86_64-pc-linux-gnu, does this look OK for trunk?

This better version of the patch uses the CRTP idiom for the class
_RangeAdaptor, and a CRTP-like idea for the class _RangeAdaptorClosure.
This latter class is now the base class of every range adaptor closure,
but it's not a class template -- its only purpose is to make ADL find
its two hidden friends functions when using the pipe operator on a range
adaptor closure, and these hidden friends are defined so that they're
only ever instantiated with the most derived class.

This version should be functionallity equivalent to the previous
version, but its simpler and gives us shorter type names (since the
_RangeAdaptor and _RangeAdaptorClosure types are now hidden as a base
class instead of being part of the name of each range adaptor).

(I previously tried using the standard CRTP idiom for this base class,
but that resulted in excessive instantiation due to each instantiation
of _RangeAdaptorClosure having its own set of partially instantiated
hidden friends.  So when performing ADL on 'r | adaptor' where adaptor
is a _Pipe<T, U>, we would find six operator| candidates:
two hidden friends from _RangeAdaptorClosure<_Pipe<T, U>>, two from
_RangeAdaptorClosure<T>, and two from _RangeAdaptorClosure<U>.  We would
then proceed to check constraints on each of them (before checking
non-dependent conversions, ever since CWG2369), which would sometimes
lead to hard errors due to excessive instantiation, as in test07 of
transform.cc.)

-- >8 --

Subject: [PATCH] libstdc++: Reimplement range adaptors [PR99433]

This rewrites our range adaptor implementation for more comprehensible
error messages, improved SFINAE behavior and conformance to P2281.

The diagnostic improvements mostly come from using appropriately named
functors instead of lambdas in the generic implementation of partial
application and composition of range adaptors, and in the definition of
each of the standard range adaptors.  This makes their pretty printed
types of much shorter and more descriptive.

The improved SFINAE behavior comes from constraining the range adaptors'
member functions appropriately.  This improvement fixes PR99433, and is
also necessary in order to implement the wording changes of P2281.

Finally, P2281 clarified that partial application and composition of
range adaptors behaves like a perfect forwarding call wrapper.  This
patch implements this, except that we don't bother adding overloads for
forwarding captured state entities as non-const lvalues, since it seems
it's sufficient to handle the const lvalue and non-const rvalue cases,
given the current set of standard range adaptors.  But such overloads
can be easily added if they turn out to be needed.

Tested on x86_64-pc-linux-gnu, does this look OK for trunk?

libstdc++-v3/ChangeLog:

	PR libstdc++/99433
	* include/std/ranges (__adaptor::__maybe_refwrap): Remove.
	(__adaptor::__adaptor_invocable): New concept.
	(__adaptor::__adaptor_partial_app_viable): New concept.
	(__adaptor::_RangeAdaptorClosure): Rewrite, turning it into a
	non-template base class.
	(__adaptor::_RangeAdaptor): Rewrite, turning it into a CRTP base
	class template.
	(__adaptor::_Partial): New class template that represents
	partial application of a range adaptor non-closure.
	(__adaptor::__pipe_invocable): New concept.
	(__adaptor::_Pipe): New class template.
	(__detail::__can_ref_view): New concept.
	(__detail::__can_subrange): New concept.
	(all): Replace the lambda here with ...
	(_All): ... this functor.  Add appropriate constraints.
	(__detail::__can_filter_view): New concept.
	(filter, _Filter): As in all/_All.
	(__detail::__can_transform): New concept.
	(transform, _Transform): As in all/_All.
	(__detail::__can_take_view): New concept.
	(take, _Take): As in all/_All.
	(__detail::__can_take_while_view): New concept.
	(take_while, _TakeWhile): As in all/_All.
	(__detail::__can_drop_view): New concept.
	(drop, _Drop): As in all/_All.
	(__detail::__can_drop_while_view): New concept.
	(drop_while, _DropWhile): As in all/_All.
	(__detail::__can_join_view): New concept.
	(join, _Join): As in all/_All.
	(__detail::__can_split_view): New concept.
	(split, _Split): As in all/_All.  Rename template parameter
	_Fp to _Pattern).
	(__detail::__already_common): New concept.
	(__detail::__can_common_view): New concept.
	(common, _Common): As in all/_All.
	(__detail::__can_reverse_view): New concept.
	(reverse, _Reverse): As in all/_All.
	(__detail::__can_elements_view): New concept.
	(elements, _Elements): As in all/_All.
	(keys, values): Adjust.
	* testsuite/std/ranges/adaptors/99433.cc: New test.
	* testsuite/std/ranges/adaptors/all.cc: No longer expect that
	adding empty range adaptor closure objects to a pipeline doesn't
	increase the size of the pipeline.
	(test05): New test.
	* testsuite/std/ranges/adaptors/common.cc (test03): New test.
	* testsuite/std/ranges/adaptors/drop.cc (test09): New test.
	* testsuite/std/ranges/adaptors/drop_while.cc (test04): New test.
	* testsuite/std/ranges/adaptors/elements.cc (test04): New test.
	* testsuite/std/ranges/adaptors/filter.cc (test06): New test.
	* testsuite/std/ranges/adaptors/join.cc (test09): New test.
	* testsuite/std/ranges/adaptors/p2281.cc: New test.
	* testsuite/std/ranges/adaptors/reverse.cc (test07): New test.
	* testsuite/std/ranges/adaptors/split.cc (test01, test04):
	Adjust.
	(test09): New test.
	* testsuite/std/ranges/adaptors/split_neg.cc (test01): Adjust
	expected error message.
	(test02): Likewise.  Extend test.
	* testsuite/std/ranges/adaptors/take.cc (test06): New test.
	* testsuite/std/ranges/adaptors/take_while.cc (test05): New test.
	* testsuite/std/ranges/adaptors/transform.cc (test07, test08):
	New test.
---
 libstdc++-v3/include/std/ranges               | 701 +++++++++++-------
 .../testsuite/std/ranges/adaptors/99433.cc    |  41 +
 .../testsuite/std/ranges/adaptors/all.cc      |  19 +
 .../testsuite/std/ranges/adaptors/common.cc   |  12 +
 .../testsuite/std/ranges/adaptors/drop.cc     |  18 +
 .../std/ranges/adaptors/drop_while.cc         |  18 +
 .../testsuite/std/ranges/adaptors/elements.cc |  12 +
 .../testsuite/std/ranges/adaptors/filter.cc   |  18 +
 .../testsuite/std/ranges/adaptors/join.cc     |  12 +
 .../testsuite/std/ranges/adaptors/p2281.cc    |  83 +++
 .../testsuite/std/ranges/adaptors/reverse.cc  |  15 +-
 .../testsuite/std/ranges/adaptors/split.cc    |  35 +-
 .../std/ranges/adaptors/split_neg.cc          |  10 +-
 .../testsuite/std/ranges/adaptors/take.cc     |  18 +
 .../std/ranges/adaptors/take_while.cc         |  18 +
 .../std/ranges/adaptors/transform.cc          |  29 +
 16 files changed, 797 insertions(+), 262 deletions(-)
 create mode 100644 libstdc++-v3/testsuite/std/ranges/adaptors/99433.cc
 create mode 100644 libstdc++-v3/testsuite/std/ranges/adaptors/p2281.cc

diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges
index adbc6d7b274..c72f87e1c5b 100644
--- a/libstdc++-v3/include/std/ranges
+++ b/libstdc++-v3/include/std/ranges
@@ -724,168 +724,172 @@ namespace __detail
 
 } // namespace __detail
 
-namespace views
+namespace views::__adaptor
 {
-  namespace __adaptor
+  // True if the range adaptor _Adaptor can be applied with _Args.
+  template<typename _Adaptor, typename... _Args>
+    concept __adaptor_invocable
+      = requires { std::declval<_Adaptor>()(declval<_Args>()...); };
+
+  // True if the range adaptor non-closure _Adaptor can be partially applied
+  // with _Args.
+  template<typename _Adaptor, typename... _Args>
+    concept __adaptor_partial_app_viable = (_Adaptor::_S_arity > 1)
+      && (sizeof...(_Args) == _Adaptor::_S_arity - 1)
+      && (constructible_from<decay_t<_Args>, _Args> && ...);
+
+  template<typename _Adaptor, typename... _Args>
+    struct _Partial;
+
+  template<typename _Lhs, typename _Rhs>
+    struct _Pipe;
+
+  // The base class of every range adaptor closure.
+  struct _RangeAdaptorClosure
   {
-    template<typename _Tp>
-      inline constexpr auto
-      __maybe_refwrap(_Tp& __arg)
-      { return reference_wrapper<_Tp>{__arg}; }
-
-    template<typename _Tp>
-      inline constexpr auto
-      __maybe_refwrap(const _Tp& __arg)
-      { return reference_wrapper<const _Tp>{__arg}; }
-
-    template<typename _Tp>
-      inline constexpr decltype(auto)
-      __maybe_refwrap(_Tp&& __arg)
-      { return std::forward<_Tp>(__arg); }
-
-    template<typename _Callable>
-      struct _RangeAdaptorClosure;
-
-    template<typename _Callable>
-      struct _RangeAdaptor
-      {
-      protected:
-	[[no_unique_address]]
-	  __detail::__maybe_present_t<!is_default_constructible_v<_Callable>,
-				      _Callable> _M_callable;
-
-      public:
-	constexpr
-	_RangeAdaptor(const _Callable& = {})
-	  requires is_default_constructible_v<_Callable>
-	{ }
-
-	constexpr
-	_RangeAdaptor(_Callable __callable)
-	  requires (!is_default_constructible_v<_Callable>)
-	  : _M_callable(std::move(__callable))
-	{ }
-
-	template<typename... _Args>
-	  requires (sizeof...(_Args) >= 1)
-	  constexpr auto
-	  operator()(_Args&&... __args) const
-	  {
-	    // [range.adaptor.object]: If a range adaptor object accepts more
-	    // than one argument, then the following expressions are equivalent:
-	    //
-	    //   (1) adaptor(range, args...)
-	    //   (2) adaptor(args...)(range)
-	    //   (3) range | adaptor(args...)
-	    //
-	    // In this case, adaptor(args...) is a range adaptor closure object.
-	    //
-	    // We handle (1) and (2) here, and (3) is just a special case of a
-	    // more general case already handled by _RangeAdaptorClosure.
-	    if constexpr (is_invocable_v<_Callable, _Args...>)
-	      {
-		static_assert(sizeof...(_Args) != 1,
-			      "a _RangeAdaptor that accepts only one argument "
-			      "should be defined as a _RangeAdaptorClosure");
-		// Here we handle adaptor(range, args...) -- just forward all
-		// arguments to the underlying adaptor routine.
-		return _Callable{}(std::forward<_Args>(__args)...);
-	      }
-	    else
-	      {
-		// Here we handle adaptor(args...)(range).
-		// Given args..., we return a _RangeAdaptorClosure that takes a
-		// range argument, such that (2) is equivalent to (1).
-		//
-		// We need to be careful about how we capture args... in this
-		// closure.  By using __maybe_refwrap, we capture lvalue
-		// references by reference (through a reference_wrapper) and
-		// otherwise capture by value.
-		auto __closure
-		  = [...__args(__maybe_refwrap(std::forward<_Args>(__args)))]
-		    <typename _Range> (_Range&& __r) {
-		      // This static_cast has two purposes: it forwards a
-		      // reference_wrapper<T> capture as a T&, and otherwise
-		      // forwards the captured argument as an rvalue.
-		      return _Callable{}(std::forward<_Range>(__r),
-			       (static_cast<unwrap_reference_t
-					    <remove_const_t<decltype(__args)>>>
-				(__args))...);
-		    };
-		using _ClosureType = decltype(__closure);
-		return _RangeAdaptorClosure<_ClosureType>(std::move(__closure));
-	      }
-	  }
-      };
-
-    template<typename _Callable>
-      _RangeAdaptor(_Callable) -> _RangeAdaptor<_Callable>;
-
-    template<typename _Callable>
-      struct _RangeAdaptorClosure : public _RangeAdaptor<_Callable>
-      {
-	using _RangeAdaptor<_Callable>::_RangeAdaptor;
-
-	template<viewable_range _Range>
-	  requires requires { declval<_Callable>()(declval<_Range>()); }
-	  constexpr auto
-	  operator()(_Range&& __r) const
-	  {
-	    if constexpr (is_default_constructible_v<_Callable>)
-	      return _Callable{}(std::forward<_Range>(__r));
-	    else
-	      return this->_M_callable(std::forward<_Range>(__r));
-	  }
-
-	template<viewable_range _Range>
-	  requires requires { declval<_Callable>()(declval<_Range>()); }
-	  friend constexpr auto
-	  operator|(_Range&& __r, const _RangeAdaptorClosure& __o)
-	  { return __o(std::forward<_Range>(__r)); }
-
-	template<typename _Tp>
-	  friend constexpr auto
-	  operator|(const _RangeAdaptorClosure<_Tp>& __x,
-		    const _RangeAdaptorClosure& __y)
-	  {
-	    if constexpr (is_default_constructible_v<_Tp>
-			  && is_default_constructible_v<_Callable>)
-	      {
-		auto __closure = [] <typename _Up> (_Up&& __e) {
-		  return std::forward<_Up>(__e) | decltype(__x){} | decltype(__y){};
-		};
-		return _RangeAdaptorClosure<decltype(__closure)>(__closure);
-	      }
-	    else if constexpr (is_default_constructible_v<_Tp>
-			       && !is_default_constructible_v<_Callable>)
-	      {
-		auto __closure = [__y] <typename _Up> (_Up&& __e) {
-		  return std::forward<_Up>(__e) | decltype(__x){} | __y;
-		};
-		return _RangeAdaptorClosure<decltype(__closure)>(__closure);
-	      }
-	    else if constexpr (!is_default_constructible_v<_Tp>
-			       && is_default_constructible_v<_Callable>)
-	      {
-		auto __closure = [__x] <typename _Up> (_Up&& __e) {
-		  return std::forward<_Up>(__e) | __x | decltype(__y){};
-		};
-		return _RangeAdaptorClosure<decltype(__closure)>(__closure);
-	      }
-	    else
-	      {
-		auto __closure = [__x, __y] <typename _Up> (_Up&& __e) {
-		  return std::forward<_Up>(__e) | __x | __y;
-		};
-		return _RangeAdaptorClosure<decltype(__closure)>(__closure);
-	      }
-	  }
-      };
-
-    template<typename _Callable>
-      _RangeAdaptorClosure(_Callable) -> _RangeAdaptorClosure<_Callable>;
-  } // namespace __adaptor
-} // namespace views
+    // range | adaptor is equivalent to adaptor(range).
+    template<typename _Self, typename _Range>
+      requires derived_from<remove_cvref_t<_Self>, _RangeAdaptorClosure>
+	&& __adaptor_invocable<_Self, _Range>
+      friend constexpr auto
+      operator|(_Range&& __r, _Self&& __self)
+      { return std::forward<_Self>(__self)(std::forward<_Range>(__r)); }
+
+    // Compose the adaptors __lhs and __rhs into a pipeline, returning
+    // another range adaptor closure object.
+    template<typename _Lhs, typename _Rhs>
+      requires derived_from<_Lhs, _RangeAdaptorClosure>
+	&& derived_from<_Rhs, _RangeAdaptorClosure>
+      friend constexpr auto
+      operator|(_Lhs __lhs, _Rhs __rhs)
+      { return _Pipe<_Lhs, _Rhs>{std::move(__lhs), std::move(__rhs)}; }
+  };
+
+  // The base class of every range adaptor non-closure.
+  //
+  // The static data member _Derived::_S_arity must contain the total number of
+  // arguments that the adaptor takes, and the class _Derived must introduce
+  // _RangeAdaptor::operator() into the class scope via a using-declaration.
+  template<typename _Derived>
+    struct _RangeAdaptor
+    {
+      // Partially apply the arguments __args to the range adaptor _Derived,
+      // returning a range adaptor closure object.
+      template<typename... _Args>
+	requires __adaptor_partial_app_viable<_Derived, _Args...>
+	constexpr auto
+	operator()(_Args&&... __args) const
+	{
+	  return _Partial<_Derived, decay_t<_Args>...>{std::forward<_Args>(__args)...};
+	}
+    };
+
+  // A range adaptor closure that represents partial application of
+  // the range adaptor _Adaptor with arguments _Args.
+  template<typename _Adaptor, typename... _Args>
+    struct _Partial : _RangeAdaptorClosure
+    {
+      tuple<_Args...> _M_args;
+
+      constexpr
+      _Partial(_Args... __args)
+	: _M_args(std::move(__args)...)
+      { }
+
+      // Invoke _Adaptor with arguments __r, _M_args.... according to the
+      // value category of the range adaptor closure object.
+      template<typename _Range>
+	requires __adaptor_invocable<_Adaptor, _Range, const _Args&...>
+	constexpr auto
+	operator()(_Range&& __r) const &
+	{
+	  auto __forwarder = [&__r] (const auto&... __args) {
+	    return _Adaptor{}(std::forward<_Range>(__r), __args...);
+	  };
+	  return std::apply(__forwarder, _M_args);
+	}
+
+      template<typename _Range>
+	requires __adaptor_invocable<_Adaptor, _Range, _Args...>
+	constexpr auto
+	operator()(_Range&& __r) &&
+	{
+	  auto __forwarder = [&__r] (auto&... __args) {
+	    return _Adaptor{}(std::forward<_Range>(__r), std::move(__args)...);
+	  };
+	  return std::apply(__forwarder, _M_args);
+	}
+
+      template<typename _Range>
+	constexpr auto
+	operator()(_Range&& __r) const && = delete;
+    };
+
+  // A lightweight partial specialization of the above primary template for
+  // the common case where _Adaptor accepts a single extra argument.
+  template<typename _Adaptor, typename _Arg>
+    struct _Partial<_Adaptor, _Arg> : _RangeAdaptorClosure
+    {
+      _Arg _M_arg;
+
+      constexpr
+      _Partial(_Arg __arg)
+	: _M_arg(std::move(__arg))
+      { }
+
+      template<typename _Range>
+	requires __adaptor_invocable<_Adaptor, _Range, const _Arg&>
+	constexpr auto
+	operator()(_Range&& __r) const &
+	{ return _Adaptor{}(std::forward<_Range>(__r), _M_arg); }
+
+      template<typename _Range>
+	requires __adaptor_invocable<_Adaptor, _Range, _Arg>
+	constexpr auto
+	operator()(_Range&& __r) &&
+	{ return _Adaptor{}(std::forward<_Range>(__r), std::move(_M_arg)); }
+
+      template<typename _Range>
+	constexpr auto
+	operator()(_Range&& __r) const && = delete;
+    };
+
+  template<typename _Lhs, typename _Rhs, typename _Range>
+    concept __pipe_invocable
+      = requires { std::declval<_Rhs>()(std::declval<_Lhs>()(std::declval<_Range>())); };
+
+  // A range adaptor closure that represents composition of the range
+  // adaptor closures _Lhs and _Rhs.
+  template<typename _Lhs, typename _Rhs>
+    struct _Pipe : _RangeAdaptorClosure
+    {
+      [[no_unique_address]] _Lhs _M_lhs;
+      [[no_unique_address]] _Rhs _M_rhs;
+
+      constexpr
+      _Pipe(_Lhs __lhs, _Rhs __rhs)
+	: _M_lhs(std::move(__lhs)), _M_rhs(std::move(__rhs))
+      { }
+
+      // Invoke _M_rhs(_M_lhs(__r)) according to the value category of this
+      // range adaptor closure object.
+      template<typename _Range>
+	requires __pipe_invocable<const _Lhs&, const _Rhs&, _Range>
+	constexpr auto
+	operator()(_Range&& __r) const &
+	{ return _M_rhs(_M_lhs(std::forward<_Range>(__r))); }
+
+      template<typename _Range>
+	requires __pipe_invocable<_Lhs, _Rhs, _Range>
+	constexpr auto
+	operator()(_Range&& __r) &&
+	{ return std::move(_M_rhs)(std::move(_M_lhs)(std::forward<_Range>(__r))); }
+
+      template<typename _Range>
+	constexpr auto
+	operator()(_Range&& __r) const && = delete;
+    };
+} // namespace views::__adaptor
 
   template<range _Range> requires is_object_v<_Range>
     class ref_view : public view_interface<ref_view<_Range>>
@@ -941,20 +945,37 @@ namespace views
 
   namespace views
   {
-    inline constexpr __adaptor::_RangeAdaptorClosure all
-      = [] <viewable_range _Range> (_Range&& __r)
-      {
-	if constexpr (view<decay_t<_Range>>)
-	  return std::forward<_Range>(__r);
-	else if constexpr (requires { ref_view{std::forward<_Range>(__r)}; })
-	  return ref_view{std::forward<_Range>(__r)};
-	else
-	  return subrange{std::forward<_Range>(__r)};
-      };
+    namespace __detail
+    {
+      template<typename _Range>
+	concept __can_ref_view = requires { ref_view{std::declval<_Range>()}; };
+
+      template<typename _Range>
+	concept __can_subrange = requires { subrange{std::declval<_Range>()}; };
+    } // namespace __detail
+
+    struct _All : __adaptor::_RangeAdaptorClosure
+    {
+      template<viewable_range _Range>
+	requires view<decay_t<_Range>>
+	  || __detail::__can_ref_view<_Range>
+	  || __detail::__can_subrange<_Range>
+	constexpr auto
+	operator()(_Range&& __r) const
+	{
+	  if constexpr (view<decay_t<_Range>>)
+	    return std::forward<_Range>(__r);
+	  else if constexpr (__detail::__can_ref_view<_Range>)
+	    return ref_view{std::forward<_Range>(__r)};
+	  else
+	    return subrange{std::forward<_Range>(__r)};
+	}
+    };
+
+    inline constexpr _All all;
 
     template<viewable_range _Range>
       using all_t = decltype(all(std::declval<_Range>()));
-
   } // namespace views
 
   // XXX: the following algos are copied from ranges_algo.h to avoid a circular
@@ -1305,11 +1326,28 @@ namespace views
 
   namespace views
   {
-    inline constexpr __adaptor::_RangeAdaptor filter
-      = [] <viewable_range _Range, typename _Pred> (_Range&& __r, _Pred&& __p)
-      {
-	return filter_view{std::forward<_Range>(__r), std::forward<_Pred>(__p)};
-      };
+    namespace __detail
+    {
+      template<typename _Range, typename _Pred>
+	concept __can_filter_view
+	  = requires { filter_view{std::declval<_Range>(), std::declval<_Pred>()}; };
+    } // namespace __detail
+
+    struct _Filter : __adaptor::_RangeAdaptor<_Filter>
+    {
+      template<viewable_range _Range, typename _Pred>
+	requires __detail::__can_filter_view<_Range, _Pred>
+	constexpr auto
+	operator()(_Range&& __r, _Pred&& __p) const
+	{
+	  return filter_view{std::forward<_Range>(__r), std::forward<_Pred>(__p)};
+	}
+
+      using _RangeAdaptor<_Filter>::operator();
+      static constexpr int _S_arity = 2;
+    };
+
+    inline constexpr _Filter filter;
   } // namespace views
 
   template<input_range _Vp, copy_constructible _Fp>
@@ -1653,11 +1691,28 @@ namespace views
 
   namespace views
   {
-    inline constexpr __adaptor::_RangeAdaptor transform
-      = [] <viewable_range _Range, typename _Fp> (_Range&& __r, _Fp&& __f)
-      {
-	return transform_view{std::forward<_Range>(__r), std::forward<_Fp>(__f)};
-      };
+    namespace __detail
+    {
+      template<typename _Range, typename _Fp>
+	concept __can_transform_view
+	  = requires { transform_view{std::declval<_Range>(), std::declval<_Fp>()}; };
+    } // namespace __detail
+
+    struct _Transform : __adaptor::_RangeAdaptor<_Transform>
+    {
+      template<viewable_range _Range, typename _Fp>
+	requires __detail::__can_transform_view<_Range, _Fp>
+	constexpr auto
+	operator()(_Range&& __r, _Fp&& __f) const
+	{
+	  return transform_view{std::forward<_Range>(__r), std::forward<_Fp>(__f)};
+	}
+
+      using _RangeAdaptor<_Transform>::operator();
+      static constexpr int _S_arity = 2;
+    };
+
+    inline constexpr _Transform transform;
   } // namespace views
 
   template<view _Vp>
@@ -1816,11 +1871,28 @@ namespace views
 
   namespace views
   {
-    inline constexpr __adaptor::_RangeAdaptor take
-      = [] <viewable_range _Range, typename _Tp> (_Range&& __r, _Tp&& __n)
-      {
-	return take_view{std::forward<_Range>(__r), std::forward<_Tp>(__n)};
-      };
+    namespace __detail
+    {
+      template<typename _Range, typename _Tp>
+	concept __can_take_view
+	  = requires { take_view{std::declval<_Range>(), std::declval<_Tp>()}; };
+    } // namespace __detail
+
+    struct _Take : __adaptor::_RangeAdaptor<_Take>
+    {
+      template<viewable_range _Range, typename _Tp>
+	requires __detail::__can_take_view<_Range, _Tp>
+	constexpr auto
+	operator()(_Range&& __r, _Tp&& __n) const
+	{
+	  return take_view{std::forward<_Range>(__r), std::forward<_Tp>(__n)};
+	}
+
+      using _RangeAdaptor<_Take>::operator();
+      static constexpr int _S_arity = 2;
+    };
+
+    inline constexpr _Take take;
   } // namespace views
 
   template<view _Vp, typename _Pred>
@@ -1918,11 +1990,28 @@ namespace views
 
   namespace views
   {
-    inline constexpr __adaptor::_RangeAdaptor take_while
-      = [] <viewable_range _Range, typename _Pred> (_Range&& __r, _Pred&& __p)
-      {
-	return take_while_view{std::forward<_Range>(__r), std::forward<_Pred>(__p)};
-      };
+    namespace __detail
+    {
+      template<typename _Range, typename _Pred>
+	concept __can_take_while_view
+	  = requires { take_while_view{std::declval<_Range>(), std::declval<_Pred>()}; };
+    } // namespace __detail
+
+    struct _TakeWhile : __adaptor::_RangeAdaptor<_TakeWhile>
+    {
+      template<viewable_range _Range, typename _Pred>
+	requires __detail::__can_take_while_view<_Range, _Pred>
+	constexpr auto
+	operator()(_Range&& __r, _Pred&& __p) const
+	{
+	  return take_while_view{std::forward<_Range>(__r), std::forward<_Pred>(__p)};
+	}
+
+      using _RangeAdaptor<_TakeWhile>::operator();
+      static constexpr int _S_arity = 2;
+    };
+
+    inline constexpr _TakeWhile take_while;
   } // namespace views
 
   template<view _Vp>
@@ -2020,11 +2109,28 @@ namespace views
 
   namespace views
   {
-    inline constexpr __adaptor::_RangeAdaptor drop
-      = [] <viewable_range _Range, typename _Tp> (_Range&& __r, _Tp&& __n)
-      {
-	return drop_view{std::forward<_Range>(__r), std::forward<_Tp>(__n)};
-      };
+    namespace __detail
+    {
+      template<typename _Range, typename _Tp>
+	concept __can_drop_view
+	  = requires { drop_view{std::declval<_Range>(), std::declval<_Tp>()}; };
+    } // namespace __detail
+
+    struct _Drop : __adaptor::_RangeAdaptor<_Drop>
+    {
+      template<viewable_range _Range, typename _Tp>
+	requires __detail::__can_drop_view<_Range, _Tp>
+	constexpr auto
+	operator()(_Range&& __r, _Tp&& __n) const
+	{
+	  return drop_view{std::forward<_Range>(__r), std::forward<_Tp>(__n)};
+	}
+
+      using _RangeAdaptor<_Drop>::operator();
+      static constexpr int _S_arity = 2;
+    };
+
+    inline constexpr _Drop drop;
   } // namespace views
 
   template<view _Vp, typename _Pred>
@@ -2085,12 +2191,29 @@ namespace views
 
   namespace views
   {
-    inline constexpr __adaptor::_RangeAdaptor drop_while
-      = [] <viewable_range _Range, typename _Pred> (_Range&& __r, _Pred&& __p)
-      {
-	return drop_while_view{std::forward<_Range>(__r),
-			       std::forward<_Pred>(__p)};
-      };
+    namespace __detail
+    {
+      template<typename _Range, typename _Pred>
+	concept __can_drop_while_view
+	  = requires { drop_while_view{std::declval<_Range>(), std::declval<_Pred>()}; };
+    } // namespace __detail
+
+    struct _DropWhile : __adaptor::_RangeAdaptor<_DropWhile>
+    {
+      template<viewable_range _Range, typename _Pred>
+	requires __detail::__can_drop_while_view<_Range, _Pred>
+	constexpr auto
+	operator()(_Range&& __r, _Pred&& __p) const
+	{
+	  return drop_while_view{std::forward<_Range>(__r),
+				 std::forward<_Pred>(__p)};
+	}
+
+      using _RangeAdaptor<_DropWhile>::operator();
+      static constexpr int _S_arity = 2;
+    };
+
+    inline constexpr _DropWhile drop_while;
   } // namespace views
 
   template<input_range _Vp>
@@ -2409,13 +2532,27 @@ namespace views
 
   namespace views
   {
-    inline constexpr __adaptor::_RangeAdaptorClosure join
-      = [] <viewable_range _Range> (_Range&& __r)
-      {
-	// _GLIBCXX_RESOLVE_LIB_DEFECTS
-	// 3474. Nesting join_views is broken because of CTAD
-	return join_view<views::all_t<_Range>>{std::forward<_Range>(__r)};
-      };
+    namespace __detail
+    {
+      template<typename _Range>
+	concept __can_join_view
+	  = requires { join_view<all_t<_Range>>{std::declval<_Range>()}; };
+    } // namespace __detail
+
+    struct _Join : __adaptor::_RangeAdaptorClosure
+    {
+      template<viewable_range _Range>
+	requires __detail::__can_join_view<_Range>
+	constexpr auto
+	operator()(_Range&& __r) const
+	{
+	  // _GLIBCXX_RESOLVE_LIB_DEFECTS
+	  // 3474. Nesting join_views is broken because of CTAD
+	  return join_view<all_t<_Range>>{std::forward<_Range>(__r)};
+	}
+    };
+
+    inline constexpr _Join join;
   } // namespace views
 
   namespace __detail
@@ -2784,9 +2921,9 @@ namespace views
       }
     };
 
-  template<typename _Range, typename _Pred>
-    split_view(_Range&&, _Pred&&)
-      -> split_view<views::all_t<_Range>, views::all_t<_Pred>>;
+  template<typename _Range, typename _Pattern>
+    split_view(_Range&&, _Pattern&&)
+      -> split_view<views::all_t<_Range>, views::all_t<_Pattern>>;
 
   template<input_range _Range>
     split_view(_Range&&, range_value_t<_Range>)
@@ -2794,11 +2931,28 @@ namespace views
 
   namespace views
   {
-    inline constexpr __adaptor::_RangeAdaptor split
-      = [] <viewable_range _Range, typename _Fp> (_Range&& __r, _Fp&& __f)
-      {
-	return split_view{std::forward<_Range>(__r), std::forward<_Fp>(__f)};
-      };
+    namespace __detail
+    {
+      template<typename _Range, typename _Pattern>
+	concept __can_split_view
+	  = requires { split_view{std::declval<_Range>(), std::declval<_Pattern>()}; };
+    } // namespace __detail
+
+    struct _Split : __adaptor::_RangeAdaptor<_Split>
+    {
+      template<viewable_range _Range, typename _Pattern>
+	requires __detail::__can_split_view<_Range, _Pattern>
+	constexpr auto
+	operator()(_Range&& __r, _Pattern&& __f) const
+	{
+	  return split_view{std::forward<_Range>(__r), std::forward<_Pattern>(__f)};
+	}
+
+      using _RangeAdaptor<_Split>::operator();
+      static constexpr int _S_arity = 2;
+    };
+
+    inline constexpr _Split split;
   } // namespace views
 
   namespace views
@@ -2911,16 +3065,33 @@ namespace views
 
   namespace views
   {
-    inline constexpr __adaptor::_RangeAdaptorClosure common
-      = [] <viewable_range _Range> (_Range&& __r)
-      {
-	if constexpr (common_range<_Range>
-		      && requires { views::all(std::forward<_Range>(__r)); })
-	  return views::all(std::forward<_Range>(__r));
-	else
-	  return common_view{std::forward<_Range>(__r)};
-      };
+    namespace __detail
+    {
+      template<typename _Range>
+	concept __already_common = common_range<_Range>
+	  && requires { views::all(std::declval<_Range>()); };
 
+      template<typename _Range>
+	concept __can_common_view
+	  = requires { common_view{std::declval<_Range>()}; };
+    } // namespace __detail
+
+    struct _Common : __adaptor::_RangeAdaptorClosure
+    {
+      template<viewable_range _Range>
+	requires __detail::__already_common<_Range>
+	  || __detail::__can_common_view<_Range>
+	constexpr auto
+	operator()(_Range&& __r) const
+	{
+	  if constexpr (__detail::__already_common<_Range>)
+	    return views::all(std::forward<_Range>(__r));
+	  else
+	    return common_view{std::forward<_Range>(__r)};
+	}
+    };
+
+    inline constexpr _Common common;
   } // namespace views
 
   template<view _Vp>
@@ -3016,27 +3187,40 @@ namespace views
 
       template<typename _Vp>
 	inline constexpr bool __is_reverse_view<reverse_view<_Vp>> = true;
-    }
 
-    inline constexpr __adaptor::_RangeAdaptorClosure reverse
-      = [] <viewable_range _Range> (_Range&& __r)
-      {
-	using _Tp = remove_cvref_t<_Range>;
-	if constexpr (__detail::__is_reverse_view<_Tp>)
-	  return std::forward<_Range>(__r).base();
-	else if constexpr (__detail::__is_reversible_subrange<_Tp>)
-	  {
-	    using _Iter = decltype(ranges::begin(__r).base());
-	    if constexpr (sized_range<_Tp>)
-	      return subrange<_Iter, _Iter, subrange_kind::sized>
-		      (__r.end().base(), __r.begin().base(), __r.size());
-	    else
-	      return subrange<_Iter, _Iter, subrange_kind::unsized>
-		      (__r.end().base(), __r.begin().base());
-	  }
-	else
-	  return reverse_view{std::forward<_Range>(__r)};
-      };
+      template<typename _Range>
+	concept __can_reverse_view
+	  = requires { reverse_view{std::declval<_Range>()}; };
+    } // namespace __detail
+
+    struct _Reverse : __adaptor::_RangeAdaptorClosure
+    {
+      template<viewable_range _Range>
+	requires __detail::__is_reverse_view<remove_cvref_t<_Range>>
+	  || __detail::__is_reversible_subrange<remove_cvref_t<_Range>>
+	  || __detail::__can_reverse_view<_Range>
+	constexpr auto
+	operator()(_Range&& __r) const
+	{
+	  using _Tp = remove_cvref_t<_Range>;
+	  if constexpr (__detail::__is_reverse_view<_Tp>)
+	    return std::forward<_Range>(__r).base();
+	  else if constexpr (__detail::__is_reversible_subrange<_Tp>)
+	    {
+	      using _Iter = decltype(ranges::begin(__r).base());
+	      if constexpr (sized_range<_Tp>)
+		return subrange<_Iter, _Iter, subrange_kind::sized>
+			{__r.end().base(), __r.begin().base(), __r.size()};
+	      else
+		return subrange<_Iter, _Iter, subrange_kind::unsized>
+			{__r.end().base(), __r.begin().base()};
+	    }
+	  else
+	    return reverse_view{std::forward<_Range>(__r)};
+	}
+    };
+
+    inline constexpr _Reverse reverse;
   } // namespace views
 
   namespace __detail
@@ -3335,16 +3519,29 @@ namespace views
 
   namespace views
   {
+    namespace __detail
+    {
+      template<size_t _Nm, typename _Range>
+	concept __can_elements_view
+	  = requires { elements_view<all_t<_Range>, _Nm>{std::declval<_Range>()}; };
+    } // namespace __detail
+
     template<size_t _Nm>
-    inline constexpr __adaptor::_RangeAdaptorClosure elements
-      = [] <viewable_range _Range> (_Range&& __r)
+      struct _Elements : __adaptor::_RangeAdaptorClosure
       {
-	using _El = elements_view<views::all_t<_Range>, _Nm>;
-	return _El{std::forward<_Range>(__r)};
+	template<viewable_range _Range>
+	  requires __detail::__can_elements_view<_Nm, _Range>
+	  constexpr auto
+	  operator()(_Range&& __r) const
+	  {
+	    return elements_view<all_t<_Range>, _Nm>{std::forward<_Range>(__r)};
+	  }
       };
 
-    inline constexpr __adaptor::_RangeAdaptorClosure keys = elements<0>;
-    inline constexpr __adaptor::_RangeAdaptorClosure values = elements<1>;
+    template<size_t _Nm>
+      inline constexpr _Elements<_Nm> elements;
+    inline constexpr auto keys = elements<0>;
+    inline constexpr auto values = elements<1>;
   } // namespace views
 
 } // namespace ranges
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/99433.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/99433.cc
new file mode 100644
index 00000000000..1fd22dfcdf2
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/99433.cc
@@ -0,0 +1,41 @@
+// Copyright (C) 2021 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+// PR libstdc++/99433
+
+#include <ranges>
+#include <vector>
+
+template <typename underlying_adaptor_t>
+struct deep
+{
+  underlying_adaptor_t adaptor;
+
+  template <typename range_t>
+  friend auto operator|(range_t &range, deep const &me)
+  {
+   return me.adaptor(range[0]);
+  }
+};
+
+auto f = [] (auto nucl) -> decltype(nucl + ' ') { return nucl + ' '; };
+auto complement = deep{std::views::transform(f)};
+std::vector<std::vector<char>> foo{};
+auto v = foo | complement;
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/all.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/all.cc
index 33e4b96686e..673bbbf4a42 100644
--- a/libstdc++-v3/testsuite/std/ranges/adaptors/all.cc
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/all.cc
@@ -103,6 +103,12 @@ static_assert(std::is_empty_v<decltype(views::common
 				       | views::common
 				       | views::keys
 				       | views::reverse)>);
+#if 0
+// Adding empty range adaptor closure objects to a pipeline used to not
+// increase the size of the pipeline, but now that the standard
+// range adaptor closure objects derive from a common empty base class,
+// [[no_unique_address]] can no longer make two empty adjacent range
+// adaptor closure objects occupy the same address.
 static_assert(sizeof(decltype(views::take(5) | views::drop(5)))
 	      == sizeof(decltype(views::take(5)
 				 | views::join
@@ -111,6 +117,18 @@ static_assert(sizeof(decltype(views::take(5) | views::drop(5)))
 				 | views::keys
 				 | views::drop(5)
 				 | views::reverse)));
+#endif
+
+template<auto all = views::all>
+void
+test05()
+{
+  // Verify SFINAE behavior.
+  static_assert(!requires { all(); });
+  static_assert(!requires { all(0, 0); });
+  static_assert(!requires { all(0); });
+  static_assert(!requires { 0 | all; });
+}
 
 int
 main()
@@ -119,4 +137,5 @@ main()
   test02();
   static_assert(test03());
   static_assert(test04());
+  test05();
 }
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/common.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/common.cc
index 719b25be006..085e8ff907d 100644
--- a/libstdc++-v3/testsuite/std/ranges/adaptors/common.cc
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/common.cc
@@ -60,9 +60,21 @@ test02()
   VERIFY( std::count(v2.begin(), v2.end(), 1) == 2);
 }
 
+template<auto common = views::common>
+void
+test03()
+{
+  // Verify SFINAE behavior.
+  static_assert(!requires { common(); });
+  static_assert(!requires { common(0, 0); });
+  static_assert(!requires { common(0); });
+  static_assert(!requires { 0 | common; });
+}
+
 int
 main()
 {
   test01();
   test02();
+  test03();
 }
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/drop.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/drop.cc
index 19afba8a9ae..c0525109bd1 100644
--- a/libstdc++-v3/testsuite/std/ranges/adaptors/drop.cc
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/drop.cc
@@ -258,6 +258,23 @@ test08()
   VERIFY( ra_test_wrapper<long>::increment_count == 0 );
 }
 
+template<auto drop = views::drop>
+void
+test09()
+{
+  // Verify SFINAE behavior.
+  extern int x[5];
+  int* n = 0;
+  static_assert(!requires { drop(); });
+  static_assert(!requires { drop(x, n, n); });
+  static_assert(!requires { drop(x, n); });
+  static_assert(!requires { drop(n)(x); });
+  static_assert(!requires { x | (drop(n) | views::all); });
+  static_assert(!requires { (drop(n) | views::all)(x); });
+  static_assert(!requires { drop | views::all; });
+  static_assert(!requires { views::all | drop; });
+}
+
 int
 main()
 {
@@ -269,4 +286,5 @@ main()
   test06();
   test07();
   test08();
+  test09();
 }
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/drop_while.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/drop_while.cc
index e807388538b..58489d54f89 100644
--- a/libstdc++-v3/testsuite/std/ranges/adaptors/drop_while.cc
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/drop_while.cc
@@ -89,6 +89,23 @@ test03()
   VERIFY( ranges::equal(v, (int[]){3,4,5}) );
 }
 
+template<auto drop_while = views::drop_while>
+void
+test04()
+{
+  // Verify SFINAE behavior.
+  extern int x[5];
+  auto p = [] (int*) { return true; };
+  static_assert(!requires { drop_while(); });
+  static_assert(!requires { drop_while(x, p, p); });
+  static_assert(!requires { drop_while(x, p); });
+  static_assert(!requires { drop_while(p)(x); });
+  static_assert(!requires { x | (drop_while(p) | views::all); });
+  static_assert(!requires { (drop_while(p) | views::all)(x); });
+  static_assert(!requires { drop_while | views::all; });
+  static_assert(!requires { views::all | drop_while; });
+}
+
 int
 main()
 {
@@ -96,4 +113,5 @@ main()
   test02();
   test03<forward_iterator_wrapper>();
   test03<random_access_iterator_wrapper>();
+  test04();
 }
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/elements.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/elements.cc
index 76807772006..b0d122f8db5 100644
--- a/libstdc++-v3/testsuite/std/ranges/adaptors/elements.cc
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/elements.cc
@@ -89,10 +89,22 @@ test03()
   VERIFY( (next(b_const, 2) - b_const) == 2 );
 }
 
+template<auto elements = views::elements<0>>
+void
+test04()
+{
+  // Verify SFINAE behavior.
+  static_assert(!requires { elements(); });
+  static_assert(!requires { elements(0, 0); });
+  static_assert(!requires { elements(0); });
+  static_assert(!requires { 0 | elements; });
+}
+
 int
 main()
 {
   test01();
   test02();
   test03();
+  test04();
 }
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/filter.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/filter.cc
index c60eec51368..e0f6b8d4c4b 100644
--- a/libstdc++-v3/testsuite/std/ranges/adaptors/filter.cc
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/filter.cc
@@ -123,6 +123,23 @@ test05()
   VERIFY( ranges::equal(v, (int[]){2,4}) );
 }
 
+template<auto filter = views::filter>
+void
+test06()
+{
+  // Verify SFINAE behavior.
+  extern int x[5];
+  auto p = [] (int*) { return true; };
+  static_assert(!requires { filter(); });
+  static_assert(!requires { filter(x, p, p); });
+  static_assert(!requires { filter(x, p); });
+  static_assert(!requires { filter(p)(x); });
+  static_assert(!requires { x | (filter(p) | views::all); });
+  static_assert(!requires { (filter(p) | views::all)(x); });
+  static_assert(!requires { filter | views::all; });
+  static_assert(!requires { views::all | filter; });
+}
+
 int
 main()
 {
@@ -132,4 +149,5 @@ main()
   test04();
   test05<forward_iterator_wrapper>();
   test05<random_access_iterator_wrapper>();
+  test06();
 }
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc
index 6d780f4887b..fb06a7698af 100644
--- a/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc
@@ -149,6 +149,17 @@ test08()
   VERIFY( i->a == 5 );
 }
 
+template<auto join = views::join>
+void
+test09()
+{
+  // Verify SFINAE behavior.
+  static_assert(!requires { join(); });
+  static_assert(!requires { join(0, 0); });
+  static_assert(!requires { join(0); });
+  static_assert(!requires { 0 | join; });
+}
+
 int
 main()
 {
@@ -160,4 +171,5 @@ main()
   test06();
   test07();
   test08();
+  test09();
 }
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/p2281.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/p2281.cc
new file mode 100644
index 00000000000..c916a5ea8b7
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/p2281.cc
@@ -0,0 +1,83 @@
+// Copyright (C) 2021 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+
+#include <algorithm>
+#include <ranges>
+#include <string>
+#include <string_view>
+#include <testsuite_hooks.h>
+
+namespace ranges = std::ranges;
+namespace views = std::ranges::views;
+
+// Verify P2281 changes to the forwarding semantics of partial application
+// and composition of range adaptor objects.
+
+void
+test01()
+{
+  auto split_into_strings = [] (auto p) {
+    return views::split(p) | views::transform([](auto r){
+      return std::string(r.begin(), ranges::next(r.begin(), r.end()));
+    });
+  };
+  constexpr std::string_view s = "hello world";
+  constexpr std::string_view p = " ";
+  constexpr auto v1 = s | split_into_strings(p);
+  constexpr auto v2 = split_into_strings(p)(s);
+  VERIFY( ranges::equal(v1, (std::string_view[]){"hello", "world"}) );
+  VERIFY( ranges::equal(v2, (std::string_view[]){"hello", "world"}) );
+}
+
+struct move_only_range
+{
+  move_only_range() { }
+  move_only_range(move_only_range&&);
+  move_only_range& operator=(move_only_range&&);
+  move_only_range(const move_only_range&) = delete;
+  move_only_range& operator=(const move_only_range&) = delete;
+  char* begin();
+  char* end();
+};
+
+template<>
+  inline constexpr bool std::ranges::enable_view<move_only_range> = true;
+
+template<auto split = views::split>
+void
+test02()
+{
+  std::string_view s;
+  move_only_range p;
+  static_assert(requires { s | split(std::move(p)); });
+  static_assert(requires { split(std::move(p))(s); });
+  static_assert(requires { split(std::move(p)) | views::all; });
+  static_assert(requires { views::all | split(std::move(p)); });
+  static_assert(!requires { split(p); });
+  static_assert(!requires { split(p) | views::all; });
+  static_assert(!requires { views::all | split(p); });
+}
+
+int
+main()
+{
+  test01();
+  test02();
+}
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/reverse.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/reverse.cc
index 0d52498e207..47e34eb6581 100644
--- a/libstdc++-v3/testsuite/std/ranges/adaptors/reverse.cc
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/reverse.cc
@@ -138,7 +138,8 @@ namespace test_ns
   void make_reverse_iterator(T&&) {}
 } // namespace test_ns
 
-void test06()
+void
+test06()
 {
   // Check that views::reverse works and does not use ADL which could lead
   // to accidentally finding test_ns::make_reverse_iterator(A*).
@@ -149,6 +150,17 @@ void test06()
   static_assert( std::ranges::range<const V> );
 }
 
+template<auto reverse = views::reverse>
+void
+test07()
+{
+  // Verify SFINAE behavior.
+  static_assert(!requires { reverse(); });
+  static_assert(!requires { reverse(0, 0); });
+  static_assert(!requires { reverse(0); });
+  static_assert(!requires { 0 | reverse; });
+}
+
 int
 main()
 {
@@ -158,4 +170,5 @@ main()
   test04();
   test05();
   test06();
+  test07();
 }
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/split.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/split.cc
index dc7f55a2f92..b9fb3728708 100644
--- a/libstdc++-v3/testsuite/std/ranges/adaptors/split.cc
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/split.cc
@@ -39,7 +39,7 @@ test01()
 {
   auto x = "the  quick  brown  fox"sv;
   auto p = std::string{"  "};
-  auto v = x | views::split(p);
+  auto v = x | views::split(views::all(p)); // views::all is needed here after P2281.
   auto i = v.begin();
   VERIFY( ranges::equal(*i++, "the"sv) );
   VERIFY( ranges::equal(*i++, "quick"sv) );
@@ -83,7 +83,7 @@ test04()
   static_assert(!ranges::view<decltype(p)>);
   static_assert(std::same_as<decltype(p | views::all),
 			     ranges::ref_view<decltype(p)>>);
-  auto v = x | views::split(p);
+  auto v = x | views::split(views::all(p)); // views::all is needed here after P2281.
   auto i = v.begin();
   VERIFY( ranges::equal(*i++, "the"sv) );
   VERIFY( ranges::equal(*i++, "quick"sv) );
@@ -152,6 +152,36 @@ test08()
   VERIFY( i == v.end() );
 }
 
+template<auto split = views::split>
+void
+test09()
+{
+  // Verify SFINAE behavior.
+  std::string s, p;
+  static_assert(!requires { split(); });
+  static_assert(!requires { split(s, p, 0); });
+  static_assert(!requires { split(p)(); });
+  static_assert(!requires { s | split; });
+
+  static_assert(!requires { s | split(p); });
+  static_assert(!requires { split(p)(s); });
+  static_assert(!requires { s | (split(p) | views::all); });
+  static_assert(!requires { (split(p) | views::all)(s); });
+
+  static_assert(requires { s | split(views::all(p)); });
+  static_assert(requires { split(views::all(p))(s); });
+  static_assert(requires { s | (split(views::all(p)) | views::all); });
+  static_assert(requires { (split(views::all(p)) | views::all)(s); });
+
+  auto adapt = split(p);
+  static_assert(requires { s | adapt; });
+  static_assert(requires { adapt(s); });
+
+  auto adapt2 = split(p) | views::all;
+  static_assert(requires { s | adapt2; });
+  static_assert(requires { adapt2(s); });
+}
+
 int
 main()
 {
@@ -163,4 +193,5 @@ main()
   test06();
   test07();
   test08();
+  test09();
 }
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/split_neg.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/split_neg.cc
index fb84049711f..4229314a9dc 100644
--- a/libstdc++-v3/testsuite/std/ranges/adaptors/split_neg.cc
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/split_neg.cc
@@ -30,8 +30,7 @@ test01()
 {
   using namespace std::literals;
   auto x = "the  quick  brown  fox"sv;
-  auto v = views::split(x, std::initializer_list<char>{' ', ' '});
-  v.begin(); // { dg-error "" }
+  auto v = views::split(x, std::initializer_list<char>{' ', ' '}); // { dg-error "no match" }
 }
 
 void
@@ -39,11 +38,8 @@ test02()
 {
   using namespace std::literals;
   auto x = "the  quick  brown  fox"sv;
-  auto v = x | views::split(std::initializer_list<char>{' ', ' '}); // { dg-error "no match" }
-  v.begin();
+  auto v1 = views::split(std::initializer_list<char>{' ', ' '})(x); // { dg-error "deleted" }
+  auto v2 = x | views::split(std::initializer_list<char>{' ', ' '}); // { dg-error "no match" }
 }
 
 // { dg-prune-output "in requirements" }
-// { dg-error "deduction failed" "" { target *-*-* } 0 }
-// { dg-error "no match" "" { target *-*-* } 0 }
-// { dg-error "constraint failure" "" { target *-*-* } 0 }
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/take.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/take.cc
index 5947c7bafab..55f74824737 100644
--- a/libstdc++-v3/testsuite/std/ranges/adaptors/take.cc
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/take.cc
@@ -100,6 +100,23 @@ test05()
   b = ranges::end(v);
 }
 
+template<auto take = views::take>
+void
+test06()
+{
+  // Verify SFINAE behavior.
+  extern int x[5];
+  int* n = 0;
+  static_assert(!requires { take(); });
+  static_assert(!requires { take(x, n, n); });
+  static_assert(!requires { take(x, n); });
+  static_assert(!requires { take(n)(x); });
+  static_assert(!requires { x | (take(n) | views::all); });
+  static_assert(!requires { (take(n) | views::all)(x); });
+  static_assert(!requires { take | views::all; });
+  static_assert(!requires { views::all | take; });
+}
+
 int
 main()
 {
@@ -108,4 +125,5 @@ main()
   test03();
   test04();
   test05();
+  test06();
 }
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/take_while.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/take_while.cc
index ff377e578c4..1ab8df3bc62 100644
--- a/libstdc++-v3/testsuite/std/ranges/adaptors/take_while.cc
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/take_while.cc
@@ -79,6 +79,23 @@ test04()
   static_assert(!ranges::range<decltype(v) const>);
 }
 
+template<auto take_while = views::take_while>
+void
+test05()
+{
+  // Verify SFINAE behavior.
+  extern int x[5];
+  auto p = [] (int*) { return true; };
+  static_assert(!requires { take_while(); });
+  static_assert(!requires { take_while(x, p, p); });
+  static_assert(!requires { take_while(x, p); });
+  static_assert(!requires { take_while(p)(x); });
+  static_assert(!requires { x | (take_while(p) | views::all); });
+  static_assert(!requires { (take_while(p) | views::all)(x); });
+  static_assert(!requires { take_while | views::all; });
+  static_assert(!requires { views::all | take_while; });
+}
+
 int
 main()
 {
@@ -86,4 +103,5 @@ main()
   test02();
   test03();
   test04();
+  test05();
 }
diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc
index 4e7e7ba0a67..ab2f28a2111 100644
--- a/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc
+++ b/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc
@@ -145,6 +145,33 @@ test06()
   VERIFY( (next(b_const, 2) - b_const) == 2 );
 }
 
+void
+test07()
+{
+  int x[] = {1,2,3,4,5};
+  auto v1 = views::transform([] (auto& x) { return &x; });
+  auto v2 = views::transform([] (auto x) { return *x; });
+  auto v = x | (v1 | v2);
+  VERIFY( ranges::equal(v, x) );
+}
+
+template<auto transform = views::transform>
+void
+test08()
+{
+  // Verify SFINAE behavior.
+  extern int x[5];
+  auto f = [] (int* e) { return e; };
+  static_assert(!requires { transform(); });
+  static_assert(!requires { transform(x, f, f); });
+  static_assert(!requires { transform(x, f); });
+  static_assert(!requires { transform(f)(x); });
+  static_assert(!requires { x | (transform(f) | views::all); });
+  static_assert(!requires { (transform(f) | views::all)(x); });
+  static_assert(!requires { transform | views::all; });
+  static_assert(!requires { views::all | transform; });
+}
+
 int
 main()
 {
@@ -154,4 +181,6 @@ main()
   test04();
   test05();
   test06();
+  test07();
+  test08();
 }
-- 
2.31.1.133.g84d06cdc06


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

* Re: [PATCH] libstdc++: Reimplement range adaptors [PR99433]
  2021-03-29 14:33 ` Patrick Palka
@ 2021-04-08 13:18   ` Jonathan Wakely
  0 siblings, 0 replies; 3+ messages in thread
From: Jonathan Wakely @ 2021-04-08 13:18 UTC (permalink / raw)
  To: Patrick Palka; +Cc: libstdc++, gcc-patches

On 29/03/21 10:33 -0400, Patrick Palka via Libstdc++ wrote:
>This rewrites our range adaptor implementation for more comprehensible
>error messages, improved SFINAE behavior and conformance to P2281.
>
>The diagnostic improvements mostly come from using appropriately named
>functors instead of lambdas in the generic implementation of partial
>application and composition of range adaptors, and in the definition of
>each of the standard range adaptors.  This makes their pretty printed
>types of much shorter and more descriptive.

s/types of/types/


>
>The improved SFINAE behavior comes from constraining the range adaptors'
>member functions appropriately.  This improvement fixes PR99433, and is
>also necessary in order to implement the wording changes of P2281.
>
>Finally, P2281 clarified that partial application and composition of
>range adaptors behaves like a perfect forwarding call wrapper.  This
>patch implements this, except that we don't bother adding overloads for
>forwarding captured state entities as non-const lvalues, since it seems
>it's sufficient to handle the const lvalue and non-const rvalue cases,
>given the current set of standard range adaptors.  But such overloads
>can be easily added if they turn out to be needed.
>
>Tested on x86_64-pc-linux-gnu, does this look OK for trunk?

OK, thanks.


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

end of thread, other threads:[~2021-04-08 13:18 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-03-29  1:58 [PATCH] libstdc++: Reimplement range adaptors [PR99433] Patrick Palka
2021-03-29 14:33 ` Patrick Palka
2021-04-08 13:18   ` 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).