Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

P2387R3 Pipe for user defined range adaptors #2661

Merged
merged 37 commits into from
Aug 22, 2022
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
341aa78
P2387R3 add bind_back
Ukilele Apr 10, 2022
ac7e672
Use cassert instead of assert.h in test. Move tests into extra function
Ukilele Apr 12, 2022
0aa40df
Merge branch 'microsoft:main' into pipe_for_user_defined_range_adaptors
Ukilele Apr 19, 2022
1f390cb
Add std::ranges::range_adaptor_closure as well as tests
Ukilele Apr 21, 2022
17f7b9a
Use strict_concepts_latest_matrix.lst
Ukilele Apr 21, 2022
ccb8f5a
clang-format stl/inc/functional
Ukilele Apr 21, 2022
82d74a5
clang-format yvals_core.h
Ukilele Apr 21, 2022
57026a2
Use correct naming convetions
Ukilele Apr 21, 2022
0a8b75c
Remove superfluous empty line
Ukilele Apr 21, 2022
fe9aaf2
Adopt test for feature_test_macro to increased value of __cpp_lib_ran…
Ukilele Apr 21, 2022
c922228
Add missing const
Ukilele Apr 21, 2022
097bde2
Define value of __cpp_lib_ranges depending on c++20 or c++23 mode
Ukilele Apr 22, 2022
2e16ab5
A function object only requires callable, not invocable
Ukilele Apr 22, 2022
0afb061
Overloads of ref-qualifier & and const&& for operator| composing two …
Ukilele Apr 22, 2022
d1a5c1b
Add __EDG__ workaround to range_adaptor_closure to mimic _Pipe::_Base
Ukilele Apr 22, 2022
55256e4
Let the standard range adaptor closure types inherit from range_adapt…
Ukilele Apr 22, 2022
383ecfa
Remove namespace _Pipe and its content
Ukilele Apr 22, 2022
cbf346a
Rename _StdPipe to _Pipe
Ukilele Apr 22, 2022
c9ac78e
Correct indentation of clang-format on comment
Ukilele Apr 22, 2022
c56d373
clang-format
Ukilele Apr 22, 2022
26d9bc5
Add tests for mixing user defined and standard range adaptor closures
Ukilele Apr 22, 2022
ababc87
Only provide and use range_adaptor_closure when _HAS_CXX23 is defined
Ukilele Apr 22, 2022
a32c71d
Fix typo Base -> _Base
Ukilele Apr 22, 2022
1ba884e
As __cpp_lib_ranges is language mode sensitive, move it to the corres…
Ukilele Apr 22, 2022
b03523e
Merge content of _Pipe_cpp23 into _Pipe
Ukilele Apr 22, 2022
5faa912
Add missing new line
Ukilele Apr 22, 2022
f191335
Add // to overcome clang-format and improve readability
Ukilele Apr 23, 2022
bb1033d
Remove std:: because of using namespace std;
Ukilele Apr 23, 2022
31f5f75
Merge remote-tracking branch 'upstream/main' into pr/Ukilele/2661
strega-nil Jul 14, 2022
3278979
Nicole suggestions
strega-nil Jul 14, 2022
3c88b92
fix formatting
strega-nil Jul 14, 2022
ecd1631
Merge branch 'main' into pipe_for_user_defined_range_adaptors
CaseyCarter Aug 18, 2022
f3cfad7
Derive _As_rvalue_fn from _Range_Adaptor_base
CaseyCarter Aug 18, 2022
42752a1
Rename `_Range_Adaptor_base` => `_Range_adaptor_base`
CaseyCarter Aug 18, 2022
c6206c4
Address review comments
CaseyCarter Aug 19, 2022
32e6cbb
Code review nitpicks: Newline, comment.
StephanTLavavej Aug 20, 2022
2aed2b5
Add `_NODISCARD` to `operator|` overloads.
StephanTLavavej Aug 20, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions stl/debugger/STL.natvis
Original file line number Diff line number Diff line change
Expand Up @@ -616,6 +616,14 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
</Expand>
</Type>

<Type Name="std::_Back_binder&lt;*&gt;">
StephanTLavavej marked this conversation as resolved.
Show resolved Hide resolved
<DisplayString>bind_back({_Mypair}, {_Mypair._Myval2,view(noparens)})</DisplayString>
<Expand>
<Item Name="[f]">_Mypair</Item>
<Item Name="[bound_args]">_Mypair._Myval2</Item>
</Expand>
</Type>


<!-- VC 2013 -->
<Type Name="std::_Mem_fn_wrap&lt;*&gt;">
Expand Down
76 changes: 76 additions & 0 deletions stl/inc/functional
Original file line number Diff line number Diff line change
Expand Up @@ -2117,6 +2117,82 @@ _NODISCARD constexpr auto bind_front(_Fx&& _Func, _Types&&... _Args) {
}
#endif // _HAS_CXX20

#if _HAS_CXX23
template <size_t... _Ix, class _Cv_FD, class _Cv_tuple_TiD, class... _Unbound>
constexpr auto _Call_back_binder(index_sequence<_Ix...>, _Cv_FD&& _Obj, _Cv_tuple_TiD&& _Tpl,
_Unbound&&... _Unbargs) noexcept(noexcept(_STD invoke(_STD forward<_Cv_FD>(_Obj),
_STD forward<_Unbound>(_Unbargs)..., _STD get<_Ix>(_STD forward<_Cv_tuple_TiD>(_Tpl))...)))
-> decltype(_STD invoke(_STD forward<_Cv_FD>(_Obj), _STD forward<_Unbound>(_Unbargs)...,
_STD get<_Ix>(_STD forward<_Cv_tuple_TiD>(_Tpl))...)) {
return _STD invoke(_STD forward<_Cv_FD>(_Obj), _STD forward<_Unbound>(_Unbargs)...,
_STD get<_Ix>(_STD forward<_Cv_tuple_TiD>(_Tpl))...);
}

template <class _Fx, class... _Types>
class _Back_binder { // wrap bound callable object and arguments
private:
using _Seq = index_sequence_for<_Types...>;

_Compressed_pair<_Fx, tuple<_Types...>> _Mypair;

_STL_INTERNAL_STATIC_ASSERT(is_same_v<_Fx, decay_t<_Fx>>);
_STL_INTERNAL_STATIC_ASSERT((is_same_v<_Types, decay_t<_Types>> && ...));

public:
template <class _FxInit, class... _TypesInit,
enable_if_t<sizeof...(_TypesInit) != 0 || !is_same_v<remove_cvref_t<_FxInit>, _Back_binder>, int> = 0>
constexpr explicit _Back_binder(_FxInit&& _Func, _TypesInit&&... _Args)
: _Mypair(_One_then_variadic_args_t{}, _STD forward<_FxInit>(_Func), _STD forward<_TypesInit>(_Args)...) {}

template <class... _Unbound>
constexpr auto operator()(_Unbound&&... _Unbargs) & noexcept(
noexcept(_Call_back_binder(_Seq{}, _Mypair._Get_first(), _Mypair._Myval2, _STD forward<_Unbound>(_Unbargs)...)))
-> decltype(_Call_back_binder(
_Seq{}, _Mypair._Get_first(), _Mypair._Myval2, _STD forward<_Unbound>(_Unbargs)...)) {
return _Call_back_binder(_Seq{}, _Mypair._Get_first(), _Mypair._Myval2, _STD forward<_Unbound>(_Unbargs)...);
}

template <class... _Unbound>
constexpr auto operator()(_Unbound&&... _Unbargs) const& noexcept(
noexcept(_Call_back_binder(_Seq{}, _Mypair._Get_first(), _Mypair._Myval2, _STD forward<_Unbound>(_Unbargs)...)))
-> decltype(_Call_back_binder(
_Seq{}, _Mypair._Get_first(), _Mypair._Myval2, _STD forward<_Unbound>(_Unbargs)...)) {
return _Call_back_binder(_Seq{}, _Mypair._Get_first(), _Mypair._Myval2, _STD forward<_Unbound>(_Unbargs)...);
}

template <class... _Unbound>
constexpr auto operator()(_Unbound&&... _Unbargs) && noexcept(noexcept(_Call_back_binder(
_Seq{}, _STD move(_Mypair._Get_first()), _STD move(_Mypair._Myval2), _STD forward<_Unbound>(_Unbargs)...)))
-> decltype(_Call_back_binder(
_Seq{}, _STD move(_Mypair._Get_first()), _STD move(_Mypair._Myval2), _STD forward<_Unbound>(_Unbargs)...)) {
return _Call_back_binder(
_Seq{}, _STD move(_Mypair._Get_first()), _STD move(_Mypair._Myval2), _STD forward<_Unbound>(_Unbargs)...);
}

template <class... _Unbound>
constexpr auto operator()(_Unbound&&... _Unbargs) const&& noexcept(noexcept(_Call_back_binder(
_Seq{}, _STD move(_Mypair._Get_first()), _STD move(_Mypair._Myval2), _STD forward<_Unbound>(_Unbargs)...)))
-> decltype(_Call_back_binder(
_Seq{}, _STD move(_Mypair._Get_first()), _STD move(_Mypair._Myval2), _STD forward<_Unbound>(_Unbargs)...)) {
return _Call_back_binder(
_Seq{}, _STD move(_Mypair._Get_first()), _STD move(_Mypair._Myval2), _STD forward<_Unbound>(_Unbargs)...);
}
};
template <class _Fx, class... _Types>
StephanTLavavej marked this conversation as resolved.
Show resolved Hide resolved
_NODISCARD constexpr auto bind_back(_Fx&& _Func, _Types&&... _Args) {
static_assert(is_constructible_v<decay_t<_Fx>, _Fx>,
"std::bind_back requires the decayed callable to be constructible from an undecayed callable");
static_assert(
is_move_constructible_v<decay_t<_Fx>>, "std::bind_back requires the decayed callable to be move constructible");
static_assert(conjunction_v<is_constructible<decay_t<_Types>, _Types>...>,
"std::bind_back requires the decayed bound arguments to be constructible from undecayed bound arguments");
static_assert(conjunction_v<is_move_constructible<decay_t<_Types>>...>,
"std::bind_back requires the decayed bound arguments to be move constructible");

return _Back_binder<decay_t<_Fx>, decay_t<_Types>...>(_STD forward<_Fx>(_Func), _STD forward<_Types>(_Args)...);
}
#endif // _HAS_CXX23

#if _HAS_FUNCTION_ALLOCATOR_SUPPORT
template <class _Fty, class _Alloc>
struct uses_allocator<function<_Fty>, _Alloc> : true_type {}; // true_type if container allocator enabled
Expand Down
193 changes: 155 additions & 38 deletions stl/inc/ranges
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,23 @@ namespace ranges {
template <bool _IsWrapped, class _Ty>
using _Maybe_wrapped = conditional_t<_IsWrapped, _Ty, _Unwrapped_t<_Ty>>;

#if _HAS_CXX23
template <class _Derived>
requires is_class_v<_Derived> && same_as<_Derived, remove_cv_t<_Derived>>
class range_adaptor_closure;

template <class _Ty>
using _Range_Adaptor_Base = range_adaptor_closure<_Ty>;
#else // ^^^ _HAS_CXX23 / _HAS_CXX20 vvv
namespace _Pipe {
template <class _Derived>
struct _Base;
} // namespace _Pipe

template <class _Ty>
using _Range_Adaptor_Base = _Pipe::_Base<_Ty>;
CaseyCarter marked this conversation as resolved.
Show resolved Hide resolved
#endif

namespace _Pipe {
// clang-format off
template <class _Left, class _Right>
Expand All @@ -72,9 +89,49 @@ namespace ranges {
&& constructible_from<remove_cvref_t<_Right>, _Right>;
// clang-format on

template <class, class>
struct _Pipeline;
template <class _ClosureLeft, class _ClosureRight>
struct _Pipeline : _Range_Adaptor_Base<_Pipeline<_ClosureLeft, _ClosureRight>> {
/* [[no_unique_address]] */ _ClosureLeft _Left;
/* [[no_unique_address]] */ _ClosureRight _Right;

template <class _Ty1, class _Ty2>
constexpr explicit _Pipeline(_Ty1&& _Val1, _Ty2&& _Val2) noexcept(
is_nothrow_constructible_v<_Ty1, _ClosureLeft>&& is_nothrow_constructible_v<_Ty2, _ClosureRight>)
CaseyCarter marked this conversation as resolved.
Show resolved Hide resolved
: _Left(_STD forward<_Ty1>(_Val1)), _Right(_STD forward<_Ty2>(_Val2)) {}

template <class _Ty>
_NODISCARD constexpr auto operator()(_Ty&& _Val) & noexcept(
CaseyCarter marked this conversation as resolved.
Show resolved Hide resolved
noexcept(_Right(_Left(_STD forward<_Ty>(_Val))))) requires requires {
_Right(_Left(_STD forward<_Ty>(_Val)));
}
{ return _Right(_Left(_STD forward<_Ty>(_Val))); }

template <class _Ty>
_NODISCARD constexpr auto operator()(_Ty&& _Val) && noexcept(
noexcept(_STD move(_Right)(_STD move(_Left)(_STD forward<_Ty>(_Val))))) requires requires {
_STD move(_Right)(_STD move(_Left)(_STD forward<_Ty>(_Val)));
}
{ return _STD move(_Right)(_STD move(_Left)(_STD forward<_Ty>(_Val))); }

template <class _Ty>
_NODISCARD constexpr auto operator()(_Ty&& _Val) const& noexcept(
noexcept(_Right(_Left(_STD forward<_Ty>(_Val))))) requires requires {
_Right(_Left(_STD forward<_Ty>(_Val)));
}
{ return _Right(_Left(_STD forward<_Ty>(_Val))); }

template <class _Ty>
_NODISCARD constexpr auto operator()(_Ty&& _Val) const&& noexcept(
noexcept(_STD move(_Right)(_STD move(_Left)(_STD forward<_Ty>(_Val))))) requires requires {
_STD move(_Right)(_STD move(_Left)(_STD forward<_Ty>(_Val)));
}
{ return _STD move(_Right)(_STD move(_Left)(_STD forward<_Ty>(_Val))); }
};

template <class _Ty1, class _Ty2>
_Pipeline(_Ty1, _Ty2) -> _Pipeline<_Ty1, _Ty2>;

#if !_HAS_CXX23
template <class _Derived>
struct _Base {
template <class _Other>
Expand Down Expand Up @@ -135,34 +192,7 @@ namespace ranges {
return static_cast<_Derived&&>(__r)(_STD forward<_Left>(__l));
}
};

template <class _Left, class _Right>
struct _Pipeline : _Base<_Pipeline<_Left, _Right>> {
/* [[no_unique_address]] */ _Left __l;
/* [[no_unique_address]] */ _Right __r;

template <class _Ty1, class _Ty2>
constexpr explicit _Pipeline(_Ty1&& _Val1, _Ty2&& _Val2) noexcept(
is_nothrow_convertible_v<_Ty1, _Left>&& is_nothrow_convertible_v<_Ty2, _Right>)
: __l(_STD forward<_Ty1>(_Val1)), __r(_STD forward<_Ty2>(_Val2)) {}

template <class _Ty>
_NODISCARD constexpr auto operator()(_Ty&& _Val) noexcept(
noexcept(__r(__l(_STD forward<_Ty>(_Val))))) requires requires {
__r(__l(static_cast<_Ty&&>(_Val)));
}
{ return __r(__l(_STD forward<_Ty>(_Val))); }

template <class _Ty>
_NODISCARD constexpr auto operator()(_Ty&& _Val) const
noexcept(noexcept(__r(__l(_STD forward<_Ty>(_Val))))) requires requires {
__r(__l(static_cast<_Ty&&>(_Val)));
}
{ return __r(__l(_STD forward<_Ty>(_Val))); }
};

template <class _Ty1, class _Ty2>
_Pipeline(_Ty1, _Ty2) -> _Pipeline<_Ty1, _Ty2>;
#endif //!_HAS_CXX23
} // namespace _Pipe

template <range _Rng, class _Derived>
Expand Down Expand Up @@ -806,7 +836,7 @@ namespace ranges {
};

template <class _Fn, class... _Types>
class _Range_closure : public _Pipe::_Base<_Range_closure<_Fn, _Types...>> {
class _Range_closure : public _Range_Adaptor_Base<_Range_closure<_Fn, _Types...>> {
public:
// We assume that _Fn is the type of a customization point object. That means
// 1. The behavior of operator() is independent of cvref qualifiers, so we can use `invocable<_Fn, ` without
Expand Down Expand Up @@ -1433,6 +1463,93 @@ namespace ranges {
inline constexpr _Istream_fn<_Ty> istream;
} // namespace views

#if _HAS_CXX23
namespace _Pipe {
template <class _Ty>
void _Derived_from_range_adaptor_closure_impl(const volatile range_adaptor_closure<_Ty>&);

template <class _Ty>
concept _Derived_from_range_adaptor_closure = requires(const volatile _Ty& __obj) {
CaseyCarter marked this conversation as resolved.
Show resolved Hide resolved
_Derived_from_range_adaptor_closure_impl(__obj);
};

template <class _Ty>
concept _Is_range_adaptor_common = derived_from<remove_cvref_t<_Ty>,
range_adaptor_closure<remove_cvref_t<_Ty>>> && _Derived_from_range_adaptor_closure<_Ty> && !range<_Ty>;
Ukilele marked this conversation as resolved.
Show resolved Hide resolved

template <class _Rng, class _Ty>
concept _Is_invocable_range_adaptor_closure =
range<_Rng> && _Can_pipe<_Rng, _Ty> && _Is_range_adaptor_common<_Ty>;

template <class _Left, class _Right>
concept _Are_composable_range_adaptor_closures =
_Is_range_adaptor_common<_Left> && _Is_range_adaptor_common<_Right> && _Can_compose<_Left, _Right>;
} // namespace _Pipe

template <class _Derived>
requires is_class_v<_Derived> && same_as<_Derived, remove_cv_t<_Derived>>
class range_adaptor_closure {
public:
template <class _Other>
requires _Pipe::_Are_composable_range_adaptor_closures<_Derived, _Other>
constexpr auto operator|(_Other&& _Right) && noexcept(
CaseyCarter marked this conversation as resolved.
Show resolved Hide resolved
noexcept(_Pipe::_Pipeline{static_cast<_Derived&&>(*this), _STD forward<_Other>(_Right)})) {
CaseyCarter marked this conversation as resolved.
Show resolved Hide resolved
return _Pipe::_Pipeline{static_cast<_Derived&&>(*this), _STD forward<_Other>(_Right)};
}

template <class _Other>
requires _Pipe::_Are_composable_range_adaptor_closures<const _Derived&, _Other>
constexpr auto operator|(_Other&& _Right) const& noexcept(
noexcept(_Pipe::_Pipeline{static_cast<const _Derived&>(*this), _STD forward<_Other>(_Right)})) {
return _Pipe::_Pipeline{static_cast<const _Derived&>(*this), _STD forward<_Other>(_Right)};
}

template <_Pipe::_Is_invocable_range_adaptor_closure<_Derived&> _Rng>
friend constexpr auto operator|(_Rng&& _Left, _Derived& _Right)
#ifdef __EDG__ // TRANSITION, VSO-1222776
noexcept(noexcept(_STD declval<_Derived&>()(_STD forward<_Rng>(_Left))))
#else // ^^^ workaround / no workaround vvv
noexcept(noexcept(static_cast<_Derived&>(_Right)(_STD forward<_Rng>(_Left))))
#endif // TRANSITION, VSO-1222776
{
return static_cast<_Derived&>(_Right)(_STD forward<_Rng>(_Left));
}

template <_Pipe::_Is_invocable_range_adaptor_closure<_Derived&&> _Rng>
friend constexpr auto operator|(_Rng&& _Left, _Derived&& _Right)
#ifdef __EDG__ // TRANSITION, VSO-1222776
noexcept(noexcept(_STD declval<_Derived>()(_STD forward<_Rng>(_Left))))
#else // ^^^ workaround / no workaround vvv
noexcept(noexcept(static_cast<_Derived&&>(_Right)(_STD forward<_Rng>(_Left))))
#endif // TRANSITION, VSO-1222776
{
return static_cast<_Derived&&>(_Right)(_STD forward<_Rng>(_Left));
}

template <_Pipe::_Is_invocable_range_adaptor_closure<const _Derived&> _Rng>
friend constexpr auto operator|(_Rng&& _Left, const _Derived& _Right)
#ifdef __EDG__ // TRANSITION, VSO-1222776
noexcept(noexcept(_STD declval<const _Derived&>()(_STD forward<_Rng>(_Left))))
#else // ^^^ workaround / no workaround vvv
noexcept(noexcept(static_cast<const _Derived&>(_Right)(_STD forward<_Rng>(_Left))))
#endif // TRANSITION, VSO-1222776
{
return static_cast<const _Derived&>(_Right)(_STD forward<_Rng>(_Left));
}

template <_Pipe::_Is_invocable_range_adaptor_closure<const _Derived&&> _Rng>
friend constexpr auto operator|(_Rng&& _Left, const _Derived&& _Right)
#ifdef __EDG__ // TRANSITION, VSO-1222776
noexcept(noexcept(_STD declval<const _Derived>()(_STD forward<_Rng>(_Left))))
#else // ^^^ workaround / no workaround vvv
noexcept(noexcept(static_cast<const _Derived&&>(_Right)(_STD forward<_Rng>(_Left))))
#endif // TRANSITION, VSO-1222776
{
return static_cast<const _Derived&&>(_Right)(_STD forward<_Rng>(_Left));
}
};
#endif //_HAS_CXX23

template <range _Rng>
requires is_object_v<_Rng>
class ref_view : public view_interface<ref_view<_Rng>> {
Expand Down Expand Up @@ -1579,7 +1696,7 @@ namespace ranges {
owning_view{static_cast<_Rng&&>(__r)};
};

class _All_fn : public _Pipe::_Base<_All_fn> {
class _All_fn : public _Range_Adaptor_Base<_All_fn> {
private:
enum class _St { _None, _View, _Ref, _Own };

Expand Down Expand Up @@ -3398,7 +3515,7 @@ namespace ranges {
explicit join_view(_Rng&&) -> join_view<views::all_t<_Rng>>;

namespace views {
class _Join_fn : public _Pipe::_Base<_Join_fn> {
class _Join_fn : public _Range_Adaptor_Base<_Join_fn> {
public:
// clang-format off
template <viewable_range _Rng>
Expand Down Expand Up @@ -3893,7 +4010,7 @@ namespace ranges {
#else // ^^^ no workaround / workaround vvv
auto _Match = _RANGES search(subrange{_Current, _Last}, _Parent->_Pattern);
auto _Begin = _Match.begin();
auto _End = _Match.end();
auto _End = _Match.end();
#endif // TRANSITION, DevCom-1559808
if (_Begin != _Last && _RANGES empty(_Parent->_Pattern)) {
++_Begin;
Expand Down Expand Up @@ -3945,7 +4062,7 @@ namespace ranges {
#else // ^^^ no workaround / workaround vvv
auto _Match = _RANGES search(subrange{_It, _Last}, _Pattern);
auto _Begin = _Match.begin();
auto _End = _Match.end();
auto _End = _Match.end();
#endif // TRANSITION, DevCom-1559808
if (_Begin != _Last && _RANGES empty(_Pattern)) {
++_Begin;
Expand Down Expand Up @@ -4155,7 +4272,7 @@ namespace ranges {
inline constexpr bool enable_borrowed_range<common_view<_Rng>> = enable_borrowed_range<_Rng>;

namespace views {
class _Common_fn : public _Pipe::_Base<_Common_fn> {
class _Common_fn : public _Range_Adaptor_Base<_Common_fn> {
private:
enum class _St { _None, _All, _Common };

Expand Down Expand Up @@ -4279,7 +4396,7 @@ namespace ranges {
reverse_view{static_cast<_Rng&&>(__r)};
};

class _Reverse_fn : public _Pipe::_Base<_Reverse_fn> {
class _Reverse_fn : public _Range_Adaptor_Base<_Reverse_fn> {
private:
enum class _St { _None, _Base, _Subrange_unsized, _Subrange_sized, _Reverse };

Expand Down Expand Up @@ -4731,7 +4848,7 @@ namespace ranges {

namespace views {
template <size_t _Index>
class _Elements_fn : public _Pipe::_Base<_Elements_fn<_Index>> {
class _Elements_fn : public _Range_Adaptor_Base<_Elements_fn<_Index>> {
public:
template <viewable_range _Rng>
_NODISCARD constexpr auto operator()(_Rng&& _Range) const noexcept(
Expand Down
Loading