[libcxx-commits] [libcxx] [libc++][ranges] P2387R3: Pipe support for user-defined range adaptors (PR #89148)
Xiaoyang Liu via libcxx-commits
libcxx-commits at lists.llvm.org
Mon Apr 22 12:35:26 PDT 2024
https://github.com/xiaoyang-sde updated https://github.com/llvm/llvm-project/pull/89148
>From f8e07ba37f87f1c104b2022b58b959032a8b20ad Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Wed, 17 Apr 2024 17:15:13 -0400
Subject: [PATCH 1/5] [libc++][ranges] P2387R3: Pipe support for user-defined
range adaptors
---
libcxx/docs/ReleaseNotes/19.rst | 1 +
libcxx/docs/Status/Cxx23.rst | 1 -
libcxx/docs/Status/Cxx23Papers.csv | 2 +-
libcxx/include/__ranges/range_adaptor.h | 25 +++-
libcxx/include/ranges | 5 +
.../range_adaptor_closure.pass.cpp | 128 ++++++++++++++++++
6 files changed, 155 insertions(+), 7 deletions(-)
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.adaptor.object/range_adaptor_closure.pass.cpp
diff --git a/libcxx/docs/ReleaseNotes/19.rst b/libcxx/docs/ReleaseNotes/19.rst
index 53cc7a77d1af48..bccbe2f10d13f0 100644
--- a/libcxx/docs/ReleaseNotes/19.rst
+++ b/libcxx/docs/ReleaseNotes/19.rst
@@ -49,6 +49,7 @@ Implemented Papers
- P2302R4 - ``std::ranges::contains``
- P1659R3 - ``std::ranges::starts_with`` and ``std::ranges::ends_with``
- P3029R1 - Better ``mdspan``'s CTAD
+- P2387R3 - Pipe support for user-defined range adaptors
Improvements and New Features
-----------------------------
diff --git a/libcxx/docs/Status/Cxx23.rst b/libcxx/docs/Status/Cxx23.rst
index b19ff4fdc0f79e..23d30c8128d71e 100644
--- a/libcxx/docs/Status/Cxx23.rst
+++ b/libcxx/docs/Status/Cxx23.rst
@@ -43,7 +43,6 @@ Paper Status
.. [#note-P0533R9] P0533R9: ``isfinite``, ``isinf``, ``isnan`` and ``isnormal`` are implemented.
.. [#note-P1413R3] P1413R3: ``std::aligned_storage_t`` and ``std::aligned_union_t`` are marked deprecated, but
clang doesn't issue a diagnostic for deprecated using template declarations.
- .. [#note-P2387R3] P2387R3: ``bind_back`` only
.. [#note-P2520R0] P2520R0: Libc++ implemented this paper as a DR in C++20 as well.
.. [#note-P2711R1] P2711R1: ``join_with_view`` hasn't been done yet since this type isn't implemented yet.
.. [#note-P2770R0] P2770R0: ``join_with_view`` hasn't been done yet since this type isn't implemented yet.
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index 065db97a0b0b15..f75dd288304b27 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -45,7 +45,7 @@
"`P1413R3 <https://wg21.link/P1413R3>`__","LWG","Deprecate ``std::aligned_storage`` and ``std::aligned_union``","February 2022","|Complete| [#note-P1413R3]_",""
"`P2255R2 <https://wg21.link/P2255R2>`__","LWG","A type trait to detect reference binding to temporary","February 2022","",""
"`P2273R3 <https://wg21.link/P2273R3>`__","LWG","Making ``std::unique_ptr`` constexpr","February 2022","|Complete|","16.0"
-"`P2387R3 <https://wg21.link/P2387R3>`__","LWG","Pipe support for user-defined range adaptors","February 2022","|Partial| [#note-P2387R3]_","","|ranges|"
+"`P2387R3 <https://wg21.link/P2387R3>`__","LWG","Pipe support for user-defined range adaptors","February 2022","|Complete|","19.0","|ranges|"
"`P2440R1 <https://wg21.link/P2440R1>`__","LWG","``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right``","February 2022","","","|ranges|"
"`P2441R2 <https://wg21.link/P2441R2>`__","LWG","``views::join_with``","February 2022","|In Progress|","","|ranges|"
"`P2442R1 <https://wg21.link/P2442R1>`__","LWG","Windowing range adaptors: ``views::chunk`` and ``views::slide``","February 2022","","","|ranges|"
diff --git a/libcxx/include/__ranges/range_adaptor.h b/libcxx/include/__ranges/range_adaptor.h
index 726b7eda019ee3..a8b33ac620c4d2 100644
--- a/libcxx/include/__ranges/range_adaptor.h
+++ b/libcxx/include/__ranges/range_adaptor.h
@@ -19,6 +19,7 @@
#include <__functional/invoke.h>
#include <__ranges/concepts.h>
#include <__type_traits/decay.h>
+#include <__type_traits/is_class.h>
#include <__type_traits/is_nothrow_constructible.h>
#include <__type_traits/remove_cvref.h>
#include <__utility/forward.h>
@@ -41,6 +42,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
// - `x | f` is equivalent to `f(x)`
// - `f1 | f2` is an adaptor closure `g` such that `g(x)` is equivalent to `f2(f1(x))`
template <class _Tp>
+ requires is_class_v<_Tp> && same_as<_Tp, remove_cv_t<_Tp>>
struct __range_adaptor_closure;
// Type that wraps an arbitrary function object and makes it into a range adaptor closure,
@@ -52,15 +54,21 @@ struct __range_adaptor_closure_t : _Fn, __range_adaptor_closure<__range_adaptor_
_LIBCPP_CTAD_SUPPORTED_FOR_TYPE(__range_adaptor_closure_t);
template <class _Tp>
-concept _RangeAdaptorClosure = derived_from<remove_cvref_t<_Tp>, __range_adaptor_closure<remove_cvref_t<_Tp>>>;
+_Tp __derived_from_range_adaptor_closure(__range_adaptor_closure<_Tp>*);
template <class _Tp>
+concept _RangeAdaptorClosure = !ranges::range<remove_cvref_t<_Tp>> && requires {
+ { __derived_from_range_adaptor_closure((remove_cvref_t<_Tp>*)nullptr) } -> same_as<remove_cvref_t<_Tp>>;
+};
+
+template <class _Tp>
+ requires is_class_v<_Tp> && same_as<_Tp, remove_cv_t<_Tp>>
struct __range_adaptor_closure {
- template <ranges::viewable_range _View, _RangeAdaptorClosure _Closure>
- requires same_as<_Tp, remove_cvref_t<_Closure>> && invocable<_Closure, _View>
+ template <ranges::range _Range, _RangeAdaptorClosure _Closure>
+ requires same_as<_Tp, remove_cvref_t<_Closure>> && invocable<_Closure, _Range>
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr decltype(auto)
- operator|(_View&& __view, _Closure&& __closure) noexcept(is_nothrow_invocable_v<_Closure, _View>) {
- return std::invoke(std::forward<_Closure>(__closure), std::forward<_View>(__view));
+ operator|(_Range&& __range, _Closure&& __closure) noexcept(is_nothrow_invocable_v<_Closure, _Range>) {
+ return std::invoke(std::forward<_Closure>(__closure), std::forward<_Range>(__range));
}
template <_RangeAdaptorClosure _Closure, _RangeAdaptorClosure _OtherClosure>
@@ -73,6 +81,13 @@ struct __range_adaptor_closure {
}
};
+# if _LIBCPP_STD_VER >= 23
+namespace ranges {
+template <class _Tp>
+using range_adaptor_closure = __range_adaptor_closure<_Tp>;
+} // namespace ranges
+# endif // _LIBCPP_STD_VER >= 23
+
#endif // _LIBCPP_STD_VER >= 20
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index 167d2137eaf454..07a525ed8641fd 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -93,6 +93,11 @@ namespace std::ranges {
template<class T>
concept viewable_range = see below;
+ // [range.adaptor.object], range adaptor objects
+ template<class D>
+ requires is_class_v<D> && same_as<D, remove_cv_t<D>>
+ class range_adaptor_closure { }; // Since c++23
+
// [view.interface], class template view_interface
template<class D>
requires is_class_v<D> && same_as<D, remove_cv_t<D>>
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adaptor.object/range_adaptor_closure.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adaptor.object/range_adaptor_closure.pass.cpp
new file mode 100644
index 00000000000000..546a8626c80e18
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.adaptor.object/range_adaptor_closure.pass.cpp
@@ -0,0 +1,128 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// std::ranges::range_adaptor_closure;
+
+#include <ranges>
+
+#include <algorithm>
+#include <vector>
+
+#include "test_range.h"
+
+using range_t = std::vector<int>;
+
+template <class T>
+concept RangeAdaptorClosure =
+ CanBePiped<range_t, T&> && CanBePiped<range_t, const T&> && CanBePiped<range_t, T&&> &&
+ CanBePiped<range_t, const T&&>;
+
+struct callable : std::ranges::range_adaptor_closure<callable> {
+ static void operator()(const range_t&) {}
+};
+static_assert(RangeAdaptorClosure<callable>);
+
+// `not_callable_1` doesn't have an `operator()`
+struct not_callable_1 : std::ranges::range_adaptor_closure<not_callable_1> {};
+static_assert(!RangeAdaptorClosure<not_callable_1>);
+
+// `not_callable_2` doesn't have an `operator()` that accepts a `range` argument
+struct not_callable_2 : std::ranges::range_adaptor_closure<not_callable_2> {
+ static void operator()() {}
+};
+static_assert(!RangeAdaptorClosure<not_callable_2>);
+
+// `not_derived_from_1` doesn't derive from `std::ranges::range_adaptor_closure`
+struct not_derived_from_1 {
+ static void operator()(const range_t&) {}
+};
+static_assert(!RangeAdaptorClosure<not_derived_from_1>);
+
+// `not_derived_from_2` doesn't publicly derive from `std::ranges::range_adaptor_closure`
+struct not_derived_from_2 : private std::ranges::range_adaptor_closure<not_derived_from_2> {
+ static void operator()(const range_t&) {}
+};
+static_assert(!RangeAdaptorClosure<not_derived_from_2>);
+
+// `not_derived_from_3` doesn't derive from the correct specialization of `std::ranges::range_adaptor_closure`
+struct not_derived_from_3 : std::ranges::range_adaptor_closure<callable> {
+ static void operator()(const range_t&) {}
+};
+static_assert(!RangeAdaptorClosure<not_derived_from_3>);
+
+// `not_derived_from_4` doesn't derive from a unique `std::ranges::range_adaptor_closure`
+struct not_derived_from_4
+ : std::ranges::range_adaptor_closure<not_derived_from_4>,
+ std::ranges::range_adaptor_closure<callable> {
+ static void operator()(const range_t&) {}
+};
+static_assert(!RangeAdaptorClosure<not_derived_from_4>);
+
+// `is_range` models `range`
+struct is_range : std::ranges::range_adaptor_closure<is_range> {
+ static void operator()(const range_t&) {}
+ int* begin() const { return nullptr; }
+ int* end() const { return nullptr; }
+};
+static_assert(std::ranges::range<is_range> && std::ranges::range<const is_range>);
+static_assert(!RangeAdaptorClosure<is_range>);
+
+// user-defined range adaptor closure object
+struct negate_fn : std::ranges::range_adaptor_closure<negate_fn> {
+ template <std::ranges::range Range>
+ static constexpr decltype(auto) operator()(Range&& range) {
+ return std::forward<Range>(range) | std::views::transform([](auto element) { return -element; });
+ }
+};
+static_assert(RangeAdaptorClosure<negate_fn>);
+constexpr auto negate = negate_fn{};
+
+// user-defined range adaptor closure object
+struct plus_1_fn : std::ranges::range_adaptor_closure<plus_1_fn> {
+ template <std::ranges::range Range>
+ static constexpr decltype(auto) operator()(Range&& range) {
+ return std::forward<Range>(range) | std::views::transform([](auto element) { return element + 1; });
+ }
+};
+static_assert(RangeAdaptorClosure<plus_1_fn>);
+constexpr auto plus_1 = plus_1_fn{};
+
+constexpr bool test() {
+ const std::vector<int> n{1, 2, 3, 4, 5};
+ const std::vector<int> n_negate{-1, -2, -3, -4, -5};
+ const std::vector<int> n_negate_plus_1{0, -1, -2, -3, -4};
+ const std::vector<int> n_plus_1_negate{-2, -3, -4, -5, -6};
+
+ assert(std::ranges::equal(n | negate, n_negate));
+ assert(std::ranges::equal(negate(n), n_negate));
+
+ assert(std::ranges::equal(n | negate | negate, n));
+ assert(std::ranges::equal(n | (negate | negate), n));
+ assert(std::ranges::equal((n | negate) | negate, n));
+ assert(std::ranges::equal(negate(n) | negate, n));
+ assert(std::ranges::equal(negate(n | negate), n));
+ assert(std::ranges::equal((negate | negate)(n), n));
+ assert(std::ranges::equal(negate(negate(n)), n));
+
+ assert(std::ranges::equal(n | negate | plus_1, n_negate_plus_1));
+ assert(std::ranges::equal(
+ n | plus_1 | std::views::transform([](auto element) { return element; }) | negate, n_plus_1_negate));
+
+ assert(std::ranges::equal(n | plus_1 | negate, n_plus_1_negate));
+ assert(std::ranges::equal(n | std::views::reverse | negate | plus_1 | std::views::reverse, n_negate_plus_1));
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
>From f5f564b56d10aaf8906848a9d3c7f56bc6920fa4 Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Wed, 17 Apr 2024 18:07:23 -0400
Subject: [PATCH 2/5] [libc++][ranges] P2387R3: Pipe support for user-defined
range adaptors
---
.../range_adaptor_closure.pass.cpp | 27 ++++++++++++++++---
1 file changed, 23 insertions(+), 4 deletions(-)
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adaptor.object/range_adaptor_closure.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adaptor.object/range_adaptor_closure.pass.cpp
index 546a8626c80e18..b0ee6db5704a6b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adaptor.object/range_adaptor_closure.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adaptor.object/range_adaptor_closure.pass.cpp
@@ -17,6 +17,20 @@
#include "test_range.h"
+template <class T>
+concept DeriveFromRangeAdaptorClosure = requires { typename std::ranges::range_adaptor_closure<T>; };
+static_assert(!DeriveFromRangeAdaptorClosure<int>);
+
+struct t {};
+static_assert(DeriveFromRangeAdaptorClosure<t>);
+static_assert(!DeriveFromRangeAdaptorClosure<t&>);
+static_assert(!DeriveFromRangeAdaptorClosure<const t>);
+static_assert(!DeriveFromRangeAdaptorClosure<volatile t>);
+static_assert(!DeriveFromRangeAdaptorClosure<const volatile t&&>);
+
+struct incomplete_t;
+static_assert(DeriveFromRangeAdaptorClosure<incomplete_t>);
+
using range_t = std::vector<int>;
template <class T>
@@ -28,6 +42,11 @@ struct callable : std::ranges::range_adaptor_closure<callable> {
static void operator()(const range_t&) {}
};
static_assert(RangeAdaptorClosure<callable>);
+static_assert(RangeAdaptorClosure<const callable>);
+static_assert(RangeAdaptorClosure<callable&>);
+static_assert(RangeAdaptorClosure<const callable&>);
+static_assert(RangeAdaptorClosure<callable&&>);
+static_assert(RangeAdaptorClosure<const callable&&>);
// `not_callable_1` doesn't have an `operator()`
struct not_callable_1 : std::ranges::range_adaptor_closure<not_callable_1> {};
@@ -97,8 +116,6 @@ constexpr auto plus_1 = plus_1_fn{};
constexpr bool test() {
const std::vector<int> n{1, 2, 3, 4, 5};
const std::vector<int> n_negate{-1, -2, -3, -4, -5};
- const std::vector<int> n_negate_plus_1{0, -1, -2, -3, -4};
- const std::vector<int> n_plus_1_negate{-2, -3, -4, -5, -6};
assert(std::ranges::equal(n | negate, n_negate));
assert(std::ranges::equal(negate(n), n_negate));
@@ -111,11 +128,13 @@ constexpr bool test() {
assert(std::ranges::equal((negate | negate)(n), n));
assert(std::ranges::equal(negate(negate(n)), n));
- assert(std::ranges::equal(n | negate | plus_1, n_negate_plus_1));
+ const std::vector<int> n_plus_1_negate{-2, -3, -4, -5, -6};
+ assert(std::ranges::equal(n | plus_1 | negate, n_plus_1_negate));
assert(std::ranges::equal(
n | plus_1 | std::views::transform([](auto element) { return element; }) | negate, n_plus_1_negate));
- assert(std::ranges::equal(n | plus_1 | negate, n_plus_1_negate));
+ const std::vector<int> n_negate_plus_1{0, -1, -2, -3, -4};
+ assert(std::ranges::equal(n | negate | plus_1, n_negate_plus_1));
assert(std::ranges::equal(n | std::views::reverse | negate | plus_1 | std::views::reverse, n_negate_plus_1));
return true;
}
>From 1f36b7db7b2feed0ed9965c00f5b8b77a720bf4a Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Wed, 17 Apr 2024 18:08:39 -0400
Subject: [PATCH 3/5] [libc++][ranges] P2387R3: Pipe support for user-defined
range adaptors
---
.../range.adaptor.object/range_adaptor_closure.pass.cpp | 5 -----
1 file changed, 5 deletions(-)
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adaptor.object/range_adaptor_closure.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adaptor.object/range_adaptor_closure.pass.cpp
index b0ee6db5704a6b..d99ab61cd47332 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adaptor.object/range_adaptor_closure.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adaptor.object/range_adaptor_closure.pass.cpp
@@ -42,11 +42,6 @@ struct callable : std::ranges::range_adaptor_closure<callable> {
static void operator()(const range_t&) {}
};
static_assert(RangeAdaptorClosure<callable>);
-static_assert(RangeAdaptorClosure<const callable>);
-static_assert(RangeAdaptorClosure<callable&>);
-static_assert(RangeAdaptorClosure<const callable&>);
-static_assert(RangeAdaptorClosure<callable&&>);
-static_assert(RangeAdaptorClosure<const callable&&>);
// `not_callable_1` doesn't have an `operator()`
struct not_callable_1 : std::ranges::range_adaptor_closure<not_callable_1> {};
>From 49e09a8dd0f4ae09ef8bc658203cb150b38bd9b8 Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Thu, 18 Apr 2024 00:29:16 -0400
Subject: [PATCH 4/5] [libc++][ranges] P2387R3: Pipe support for user-defined
range adaptors
---
libcxx/include/__ranges/range_adaptor.h | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/libcxx/include/__ranges/range_adaptor.h b/libcxx/include/__ranges/range_adaptor.h
index a8b33ac620c4d2..02514148d89a69 100644
--- a/libcxx/include/__ranges/range_adaptor.h
+++ b/libcxx/include/__ranges/range_adaptor.h
@@ -84,7 +84,8 @@ struct __range_adaptor_closure {
# if _LIBCPP_STD_VER >= 23
namespace ranges {
template <class _Tp>
-using range_adaptor_closure = __range_adaptor_closure<_Tp>;
+ requires is_class_v<_Tp> && same_as<_Tp, remove_cv_t<_Tp>>
+class range_adaptor_closure : public __range_adaptor_closure<_Tp> {};
} // namespace ranges
# endif // _LIBCPP_STD_VER >= 23
>From 90fe07ecd3c05a9c873f0bdb189a447eeb80772b Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Mon, 22 Apr 2024 15:35:07 -0400
Subject: [PATCH 5/5] [libc++][ranges] P2387R3: Pipe support for user-defined
range adaptors
---
libcxx/docs/Status/RangesMajorFeatures.csv | 2 +-
libcxx/include/__ranges/range_adaptor.h | 43 ++++++++++---------
.../range_adaptor_closure.pass.cpp | 20 ++++-----
3 files changed, 34 insertions(+), 31 deletions(-)
diff --git a/libcxx/docs/Status/RangesMajorFeatures.csv b/libcxx/docs/Status/RangesMajorFeatures.csv
index c0bec8d924e8a9..d00fbce9edf489 100644
--- a/libcxx/docs/Status/RangesMajorFeatures.csv
+++ b/libcxx/docs/Status/RangesMajorFeatures.csv
@@ -1,5 +1,5 @@
Standard,Name,Assignee,CL,Status
C++23,`ranges::to <https://wg21.link/P1206R7>`_,Konstantin Varlamov,`D142335 <https://reviews.llvm.org/D142335>`_,Complete
-C++23,`Pipe support for user-defined range adaptors <https://wg21.link/P2387R3>`_,Unassigned,No patch yet,Not started
+C++23,`Pipe support for user-defined range adaptors <https://wg21.link/P2387R3>`_,"Louis Dionne, Jakub Mazurkiewicz, and Xiaoyang Liu",Various,Complete
C++23,`Formatting Ranges <https://wg21.link/P2286R8>`_,Mark de Wever,Various,Complete
C++20,`Stashing stashing iterators for proper flattening <https://wg21.link/P2770R0>`_,Jakub Mazurkiewicz,Various,In progress
diff --git a/libcxx/include/__ranges/range_adaptor.h b/libcxx/include/__ranges/range_adaptor.h
index 02514148d89a69..2da246f24e1d2f 100644
--- a/libcxx/include/__ranges/range_adaptor.h
+++ b/libcxx/include/__ranges/range_adaptor.h
@@ -36,6 +36,8 @@ _LIBCPP_BEGIN_NAMESPACE_STD
#if _LIBCPP_STD_VER >= 20
+namespace ranges {
+
// CRTP base that one can derive from in order to be considered a range adaptor closure
// by the library. When deriving from this class, a pipe operator will be provided to
// make the following hold:
@@ -58,37 +60,38 @@ _Tp __derived_from_range_adaptor_closure(__range_adaptor_closure<_Tp>*);
template <class _Tp>
concept _RangeAdaptorClosure = !ranges::range<remove_cvref_t<_Tp>> && requires {
- { __derived_from_range_adaptor_closure((remove_cvref_t<_Tp>*)nullptr) } -> same_as<remove_cvref_t<_Tp>>;
+ // Ensure that `remove_cvref_t<_Tp>` is derived from `__range_adaptor_closure<remove_cvref_t<_Tp>>` and isn't derived
+ // from `__range_adaptor_closure<U>` for any other type `U`.
+ { ranges::__derived_from_range_adaptor_closure((remove_cvref_t<_Tp>*)nullptr) } -> same_as<remove_cvref_t<_Tp>>;
};
+template <ranges::range _Range, _RangeAdaptorClosure _Closure>
+ requires invocable<_Closure, _Range>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto)
+operator|(_Range&& __range, _Closure&& __closure) noexcept(is_nothrow_invocable_v<_Closure, _Range>) {
+ return std::invoke(std::forward<_Closure>(__closure), std::forward<_Range>(__range));
+}
+
+template <_RangeAdaptorClosure _Closure, _RangeAdaptorClosure _OtherClosure>
+ requires constructible_from<decay_t<_Closure>, _Closure> && constructible_from<decay_t<_OtherClosure>, _OtherClosure>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator|(_Closure&& __c1, _OtherClosure&& __c2) noexcept(
+ is_nothrow_constructible_v<decay_t<_Closure>, _Closure> &&
+ is_nothrow_constructible_v<decay_t<_OtherClosure>, _OtherClosure>) {
+ return __range_adaptor_closure_t(std::__compose(std::forward<_OtherClosure>(__c2), std::forward<_Closure>(__c1)));
+}
+
template <class _Tp>
requires is_class_v<_Tp> && same_as<_Tp, remove_cv_t<_Tp>>
-struct __range_adaptor_closure {
- template <ranges::range _Range, _RangeAdaptorClosure _Closure>
- requires same_as<_Tp, remove_cvref_t<_Closure>> && invocable<_Closure, _Range>
- [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr decltype(auto)
- operator|(_Range&& __range, _Closure&& __closure) noexcept(is_nothrow_invocable_v<_Closure, _Range>) {
- return std::invoke(std::forward<_Closure>(__closure), std::forward<_Range>(__range));
- }
-
- template <_RangeAdaptorClosure _Closure, _RangeAdaptorClosure _OtherClosure>
- requires same_as<_Tp, remove_cvref_t<_Closure>> && constructible_from<decay_t<_Closure>, _Closure> &&
- constructible_from<decay_t<_OtherClosure>, _OtherClosure>
- [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr auto operator|(_Closure&& __c1, _OtherClosure&& __c2) noexcept(
- is_nothrow_constructible_v<decay_t<_Closure>, _Closure> &&
- is_nothrow_constructible_v<decay_t<_OtherClosure>, _OtherClosure>) {
- return __range_adaptor_closure_t(std::__compose(std::forward<_OtherClosure>(__c2), std::forward<_Closure>(__c1)));
- }
-};
+struct __range_adaptor_closure {};
# if _LIBCPP_STD_VER >= 23
-namespace ranges {
template <class _Tp>
requires is_class_v<_Tp> && same_as<_Tp, remove_cv_t<_Tp>>
class range_adaptor_closure : public __range_adaptor_closure<_Tp> {};
-} // namespace ranges
# endif // _LIBCPP_STD_VER >= 23
+} // namespace ranges
+
#endif // _LIBCPP_STD_VER >= 20
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/test/std/ranges/range.adaptors/range.adaptor.object/range_adaptor_closure.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.adaptor.object/range_adaptor_closure.pass.cpp
index d99ab61cd47332..9d1eb124345813 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.adaptor.object/range_adaptor_closure.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.adaptor.object/range_adaptor_closure.pass.cpp
@@ -18,18 +18,18 @@
#include "test_range.h"
template <class T>
-concept DeriveFromRangeAdaptorClosure = requires { typename std::ranges::range_adaptor_closure<T>; };
-static_assert(!DeriveFromRangeAdaptorClosure<int>);
+concept CanDeriveFromRangeAdaptorClosure = requires { typename std::ranges::range_adaptor_closure<T>; };
+static_assert(!CanDeriveFromRangeAdaptorClosure<int>);
-struct t {};
-static_assert(DeriveFromRangeAdaptorClosure<t>);
-static_assert(!DeriveFromRangeAdaptorClosure<t&>);
-static_assert(!DeriveFromRangeAdaptorClosure<const t>);
-static_assert(!DeriveFromRangeAdaptorClosure<volatile t>);
-static_assert(!DeriveFromRangeAdaptorClosure<const volatile t&&>);
+struct Foo {};
+static_assert(CanDeriveFromRangeAdaptorClosure<Foo>);
+static_assert(!CanDeriveFromRangeAdaptorClosure<Foo&>);
+static_assert(!CanDeriveFromRangeAdaptorClosure<const Foo>);
+static_assert(!CanDeriveFromRangeAdaptorClosure<volatile Foo>);
+static_assert(!CanDeriveFromRangeAdaptorClosure<const volatile Foo&&>);
struct incomplete_t;
-static_assert(DeriveFromRangeAdaptorClosure<incomplete_t>);
+static_assert(CanDeriveFromRangeAdaptorClosure<incomplete_t>);
using range_t = std::vector<int>;
@@ -71,7 +71,7 @@ struct not_derived_from_3 : std::ranges::range_adaptor_closure<callable> {
};
static_assert(!RangeAdaptorClosure<not_derived_from_3>);
-// `not_derived_from_4` doesn't derive from a unique `std::ranges::range_adaptor_closure`
+// `not_derived_from_4` doesn't derive from exactly one specialization of `std::ranges::range_adaptor_closure`
struct not_derived_from_4
: std::ranges::range_adaptor_closure<not_derived_from_4>,
std::ranges::range_adaptor_closure<callable> {
More information about the libcxx-commits
mailing list