From 9e89f5ae6fc409cc133d37f66406d170581e9f84 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Fri, 10 Sep 2021 10:11:33 -0700 Subject: [PATCH 1/4] Implement P2281 Which explicitly specifies that range adaptor closure objects are perfect-forwarding call wrappers, which are more fastidious about value categories of bound arguments than were our ad-hoc `_Partial`ly-applied functions. Fixes #1983 --- stl/inc/ranges | 366 ++++++------------ stl/inc/yvals_core.h | 1 + .../P0896R4_ranges_range_machinery/test.cpp | 44 +++ tests/std/tests/P0896R4_views_join/test.cpp | 4 +- 4 files changed, 163 insertions(+), 252 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 3b90541501..1b8da3a6ce 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -17,6 +17,7 @@ #include #include #include +#include #pragma pack(push, _CRT_PACKING) #pragma warning(push, _STL_WARNING_LEVEL) @@ -776,6 +777,63 @@ namespace ranges { }; }; + template + class _Range_closure : public _Pipe::_Base<_Range_closure<_Fn, _Types...>> { + public: + _STL_INTERNAL_STATIC_ASSERT((same_as, _Types> && ...)); + _STL_INTERNAL_STATIC_ASSERT(is_empty_v<_Fn>&& is_default_constructible_v<_Fn>); + + template + constexpr explicit _Range_closure(_UTypes&&... _Args) noexcept( + (is_nothrow_constructible_v<_Types, _UTypes> && ...)) + : _Captures(_STD forward<_UTypes>(_Args)...) { + _STL_INTERNAL_STATIC_ASSERT((same_as, _Types> && ...)); + } + + void operator()(auto&&) & = delete; + void operator()(auto&&) const& = delete; + void operator()(auto&&) && = delete; + void operator()(auto&&) const&& = delete; + + template + requires invocable + constexpr decltype(auto) operator()(_Ty&& _Arg) & noexcept( + noexcept(_Call(_STD forward<_Ty>(_Arg), index_sequence_for<_Types...>{}, _Captures))) { + return _Call(_STD forward<_Ty>(_Arg), index_sequence_for<_Types...>{}, _Captures); + } + + template + requires invocable + constexpr decltype(auto) operator()(_Ty&& _Arg) const& noexcept( + noexcept(_Call(_STD forward<_Ty>(_Arg), index_sequence_for<_Types...>{}, _Captures))) { + return _Call(_STD forward<_Ty>(_Arg), index_sequence_for<_Types...>{}, _Captures); + } + + template + requires invocable + constexpr decltype(auto) operator()(_Ty&& _Arg) && noexcept( + noexcept(_Call(_STD forward<_Ty>(_Arg), index_sequence_for<_Types...>{}, _STD move(_Captures)))) { + return _Call(_STD forward<_Ty>(_Arg), index_sequence_for<_Types...>{}, _STD move(_Captures)); + } + + template + requires invocable + constexpr decltype(auto) operator()(_Ty&& _Arg) const&& noexcept( + noexcept(_Call(_STD forward<_Ty>(_Arg), index_sequence_for<_Types...>{}, _STD move(_Captures)))) { + return _Call(_STD forward<_Ty>(_Arg), index_sequence_for<_Types...>{}, _STD move(_Captures)); + } + + private: + template + static constexpr decltype(auto) _Call(_Ty&& _Arg, index_sequence<_Idx...>, _Tuple&& _Captures) noexcept( + noexcept(_Fn{}(_STD forward<_Ty>(_Arg), _STD get<_Idx>(_STD forward<_Tuple>(_Captures))...))) { + _STL_INTERNAL_STATIC_ASSERT(same_as, index_sequence_for<_Types...>>); + return _Fn{}(_STD forward<_Ty>(_Arg), _STD get<_Idx>(_STD forward<_Tuple>(_Captures))...); + } + + tuple<_Types...> _Captures; + }; + // clang-format off template requires is_object_v<_Ty> @@ -1658,41 +1716,24 @@ namespace ranges { filter_view(_Rng&&, _Pr) -> filter_view, _Pr>; namespace views { - class _Filter_fn { - private: - template - struct _Partial : _Pipe::_Base<_Partial<_Pr>> { - /* [[no_unique_address]] */ _Copyable_box<_Pr> _Pred; - - template - _NODISCARD constexpr auto operator()(_Rng&& _Range) const& noexcept( - noexcept(filter_view(_STD forward<_Rng>(_Range), *_Pred))) requires requires { - filter_view(static_cast<_Rng&&>(_Range), *_Pred); - } - { return filter_view(_STD forward<_Rng>(_Range), *_Pred); } - - template - _NODISCARD constexpr auto operator()(_Rng&& _Range) && noexcept( - noexcept(filter_view(_STD forward<_Rng>(_Range), _STD move(*_Pred)))) requires requires { - filter_view(static_cast<_Rng&&>(_Range), _STD move(*_Pred)); - } - { return filter_view(_STD forward<_Rng>(_Range), _STD move(*_Pred)); } - }; - - public: + struct _Filter_fn { // clang-format off template - _NODISCARD constexpr auto operator()(_Rng&& _Range, _Pr _Pred) const noexcept(noexcept( - filter_view(_STD forward<_Rng>(_Range), _STD move(_Pred)))) requires requires { - filter_view(static_cast<_Rng&&>(_Range), _STD move(_Pred)); + _NODISCARD constexpr auto operator()(_Rng&& _Range, _Pr&& _Pred) const noexcept(noexcept( + filter_view(_STD forward<_Rng>(_Range), _STD forward<_Pr>(_Pred)))) requires requires { + filter_view(static_cast<_Rng&&>(_Range), _STD forward<_Pr>(_Pred)); } { // clang-format on - return filter_view(_STD forward<_Rng>(_Range), _STD move(_Pred)); + return filter_view(_STD forward<_Rng>(_Range), _STD forward<_Pr>(_Pred)); } - template <_Copy_constructible_object _Pr> - _NODISCARD constexpr auto operator()(_Pr _Pred) const noexcept(is_nothrow_move_constructible_v<_Pr>) { - return _Partial<_Pr>{._Pred = {in_place, _STD move(_Pred)}}; + // clang-format off + template + requires constructible_from, _Pr> + _NODISCARD constexpr auto operator()(_Pr&& _Pred) const + noexcept(is_nothrow_constructible_v, _Pr>) { + // clang-format on + return _Range_closure<_Filter_fn, decay_t<_Pr>>{_STD forward<_Pr>(_Pred)}; } }; @@ -2143,28 +2184,7 @@ namespace ranges { transform_view(_Rng&&, _Fn) -> transform_view, _Fn>; namespace views { - class _Transform_fn { - private: - template - struct _Partial : _Pipe::_Base<_Partial<_Fn>> { - /* [[no_unique_address]] */ _Copyable_box<_Fn> _Fun; - - template - _NODISCARD constexpr auto operator()(_Rng&& _Range) const& noexcept( - noexcept(transform_view(_STD forward<_Rng>(_Range), *_Fun))) requires requires { - transform_view(static_cast<_Rng&&>(_Range), *_Fun); - } - { return transform_view(_STD forward<_Rng>(_Range), *_Fun); } - - template - _NODISCARD constexpr auto operator()(_Rng&& _Range) && noexcept( - noexcept(transform_view(_STD forward<_Rng>(_Range), _STD move(*_Fun)))) requires requires { - transform_view(static_cast<_Rng&&>(_Range), _STD move(*_Fun)); - } - { return transform_view(_STD forward<_Rng>(_Range), _STD move(*_Fun)); } - }; - - public: + struct _Transform_fn { // clang-format off template _NODISCARD constexpr auto operator()(_Rng&& _Range, _Fn _Fun) const noexcept(noexcept( @@ -2175,9 +2195,13 @@ namespace ranges { return transform_view(_STD forward<_Rng>(_Range), _STD move(_Fun)); } - template <_Copy_constructible_object _Fn> - _NODISCARD constexpr auto operator()(_Fn _Fun) const noexcept(is_nothrow_move_constructible_v<_Fn>) { - return _Partial<_Fn>{._Fun = {in_place, _STD move(_Fun)}}; + // clang-format off + template + requires constructible_from, _Fn> + _NODISCARD constexpr auto operator()(_Fn&& _Fun) const + noexcept(is_nothrow_constructible_v, _Fn>) { + // clang-format on + return _Range_closure<_Transform_fn, decay_t<_Fn>>{_STD forward<_Fn>(_Fun)}; } }; @@ -2394,51 +2418,6 @@ namespace ranges { template static constexpr _Choice_t<_St> _Choice = _Choose<_Rng>(); - template - struct _Partial : _Pipe::_Base<_Partial<_Ty>> { - _Ty _Length; - - // clang-format off - template - requires convertible_to<_Ty&, range_difference_t<_Rng>> - _NODISCARD constexpr auto operator()(_Rng&& _Range) & - noexcept(noexcept(_Take_fn{}(_STD forward<_Rng>(_Range), _Length))) { - // clang-format on - _STL_INTERNAL_STATIC_ASSERT(is_aggregate_v<_Partial>); - return _Take_fn{}(_STD forward<_Rng>(_Range), _Length); - } - - // clang-format off - template - requires convertible_to> - _NODISCARD constexpr auto operator()(_Rng&& _Range) const& - noexcept(noexcept(_Take_fn{}(_STD forward<_Rng>(_Range), _Length))) { - // clang-format on - _STL_INTERNAL_STATIC_ASSERT(is_aggregate_v<_Partial>); - return _Take_fn{}(_STD forward<_Rng>(_Range), _Length); - } - - // clang-format off - template - requires convertible_to<_Ty, range_difference_t<_Rng>> - _NODISCARD constexpr auto operator()(_Rng&& _Range) && - noexcept(noexcept(_Take_fn{}(_STD forward<_Rng>(_Range), _STD move(_Length)))) { - // clang-format on - _STL_INTERNAL_STATIC_ASSERT(is_aggregate_v<_Partial>); - return _Take_fn{}(_STD forward<_Rng>(_Range), _STD move(_Length)); - } - - // clang-format off - template - requires convertible_to> - _NODISCARD constexpr auto operator()(_Rng&& _Range) const&& - noexcept(noexcept(_Take_fn{}(_STD forward<_Rng>(_Range), _STD move(_Length)))) { - // clang-format on - _STL_INTERNAL_STATIC_ASSERT(is_aggregate_v<_Partial>); - return _Take_fn{}(_STD forward<_Rng>(_Range), _STD move(_Length)); - } - }; - public: template _NODISCARD constexpr auto operator()(_Rng&& _Range, range_difference_t<_Rng> _Count) const @@ -2471,9 +2450,13 @@ namespace ranges { } } - template - _NODISCARD constexpr auto operator()(_Ty _Length) const noexcept { - return _Partial<_Ty>{._Length = _STD move(_Length)}; + // clang-format off + template + requires constructible_from, _Ty> + _NODISCARD constexpr auto operator()(_Ty&& _Length) const + noexcept(is_nothrow_constructible_v, _Ty>) { + // clang-format on + return _Range_closure<_Take_fn, decay_t<_Ty>>{_STD forward<_Ty>(_Length)}; } }; @@ -2627,28 +2610,7 @@ namespace ranges { take_while_view(_Rng&&, _Pr) -> take_while_view, _Pr>; namespace views { - class _Take_while_fn { - private: - template - struct _Partial : _Pipe::_Base<_Partial<_Pr>> { - /* [[no_unique_address]] */ _Copyable_box<_Pr> _Pred; - - template - _NODISCARD constexpr auto operator()(_Rng&& _Range) const& noexcept( - noexcept(take_while_view(_STD forward<_Rng>(_Range), *_Pred))) requires requires { - take_while_view(static_cast<_Rng&&>(_Range), *_Pred); - } - { return take_while_view(_STD forward<_Rng>(_Range), *_Pred); } - - template - _NODISCARD constexpr auto operator()(_Rng&& _Range) && noexcept( - noexcept(take_while_view(_STD forward<_Rng>(_Range), _STD move(*_Pred)))) requires requires { - take_while_view(static_cast<_Rng&&>(_Range), _STD move(*_Pred)); - } - { return take_while_view(_STD forward<_Rng>(_Range), _STD move(*_Pred)); } - }; - - public: + struct _Take_while_fn { template _NODISCARD constexpr auto operator()(_Rng&& _Range, _Pr _Pred) const noexcept(noexcept(take_while_view(_STD forward<_Rng>(_Range), _STD move(_Pred)))) requires requires { @@ -2656,9 +2618,13 @@ namespace ranges { } { return take_while_view(_STD forward<_Rng>(_Range), _STD move(_Pred)); } - template <_Copy_constructible_object _Pr> - _NODISCARD constexpr auto operator()(_Pr _Pred) const noexcept(is_nothrow_move_constructible_v<_Pr>) { - return _Partial<_Pr>{._Pred = {in_place, _STD move(_Pred)}}; + // clang-format off + template + requires constructible_from, _Pr> + _NODISCARD constexpr auto operator()(_Pr&& _Pred) const + noexcept(is_nothrow_constructible_v, _Pr>) { + // clang-format on + return _Range_closure<_Take_while_fn, decay_t<_Pr>>{_STD forward<_Pr>(_Pred)}; } }; @@ -2823,51 +2789,6 @@ namespace ranges { template static constexpr _Choice_t<_St> _Choice = _Choose<_Rng>(); - template - struct _Partial : _Pipe::_Base<_Partial<_Ty>> { - _Ty _Length; - - // clang-format off - template - requires convertible_to<_Ty&, range_difference_t<_Rng>> - _NODISCARD constexpr auto operator()(_Rng&& _Range) & - noexcept(noexcept(_Drop_fn{}(_STD forward<_Rng>(_Range), _Length))) { - // clang-format on - _STL_INTERNAL_STATIC_ASSERT(is_aggregate_v<_Partial>); - return _Drop_fn{}(_STD forward<_Rng>(_Range), _Length); - } - - // clang-format off - template - requires convertible_to> - _NODISCARD constexpr auto operator()(_Rng&& _Range) const& - noexcept(noexcept(_Drop_fn{}(_STD forward<_Rng>(_Range), _Length))) { - // clang-format on - _STL_INTERNAL_STATIC_ASSERT(is_aggregate_v<_Partial>); - return _Drop_fn{}(_STD forward<_Rng>(_Range), _Length); - } - - // clang-format off - template - requires convertible_to<_Ty, range_difference_t<_Rng>> - _NODISCARD constexpr auto operator()(_Rng&& _Range) && - noexcept(noexcept(_Drop_fn{}(_STD forward<_Rng>(_Range), _STD move(_Length)))) { - // clang-format on - _STL_INTERNAL_STATIC_ASSERT(is_aggregate_v<_Partial>); - return _Drop_fn{}(_STD forward<_Rng>(_Range), _STD move(_Length)); - } - - // clang-format off - template - requires convertible_to> - _NODISCARD constexpr auto operator()(_Rng&& _Range) const&& - noexcept(noexcept(_Drop_fn{}(_STD forward<_Rng>(_Range), _STD move(_Length)))) { - // clang-format on - _STL_INTERNAL_STATIC_ASSERT(is_aggregate_v<_Partial>); - return _Drop_fn{}(_STD forward<_Rng>(_Range), _STD move(_Length)); - } - }; - public: template _NODISCARD constexpr auto operator()(_Rng&& _Range, range_difference_t<_Rng> _Count) const @@ -2901,9 +2822,13 @@ namespace ranges { } } - template - _NODISCARD constexpr auto operator()(_Ty _Length) const noexcept { - return _Partial<_Ty>{._Length = _STD move(_Length)}; + // clang-format off + template + requires constructible_from, _Ty> + _NODISCARD constexpr auto operator()(_Ty&& _Length) const + noexcept(is_nothrow_constructible_v, _Ty>) { + // clang-format on + return _Range_closure<_Drop_fn, decay_t<_Ty>>{_STD forward<_Ty>(_Length)}; } }; @@ -2974,28 +2899,7 @@ namespace ranges { inline constexpr bool enable_borrowed_range> = enable_borrowed_range<_Rng>; namespace views { - class _Drop_while_fn { - private: - template - struct _Partial : _Pipe::_Base<_Partial<_Pr>> { - /* [[no_unique_address]] */ _Copyable_box<_Pr> _Pred; - - template - _NODISCARD constexpr auto operator()(_Rng&& _Range) const& noexcept( - noexcept(drop_while_view(_STD forward<_Rng>(_Range), *_Pred))) requires requires { - drop_while_view(static_cast<_Rng&&>(_Range), *_Pred); - } - { return drop_while_view(_STD forward<_Rng>(_Range), *_Pred); } - - template - _NODISCARD constexpr auto operator()(_Rng&& _Range) && noexcept( - noexcept(drop_while_view(_STD forward<_Rng>(_Range), _STD move(*_Pred)))) requires requires { - drop_while_view(static_cast<_Rng&&>(_Range), _STD move(*_Pred)); - } - { return drop_while_view(_STD forward<_Rng>(_Range), _STD move(*_Pred)); } - }; - - public: + struct _Drop_while_fn { template _NODISCARD constexpr auto operator()(_Rng&& _Range, _Pr _Pred) const noexcept(noexcept(drop_while_view(_STD forward<_Rng>(_Range), _STD move(_Pred)))) requires requires { @@ -3003,9 +2907,13 @@ namespace ranges { } { return drop_while_view(_STD forward<_Rng>(_Range), _STD move(_Pred)); } - template <_Copy_constructible_object _Pr> - _NODISCARD constexpr auto operator()(_Pr _Pred) const noexcept(is_nothrow_move_constructible_v<_Pr>) { - return _Partial<_Pr>{._Pred = {in_place, _STD move(_Pred)}}; + // clang-format off + template + requires constructible_from, _Pr> + _NODISCARD constexpr auto operator()(_Pr&& _Pred) const + noexcept(is_nothrow_constructible_v, _Pr>) { + // clang-format on + return _Range_closure<_Drop_while_fn, decay_t<_Pr>>{_STD forward<_Pr>(_Pred)}; } }; @@ -3772,28 +3680,7 @@ namespace ranges { -> lazy_split_view, single_view>>; namespace views { - class _Lazy_split_fn { - private: - template - struct _Partial : _Pipe::_Base<_Partial<_Delim>> { - /* [[no_unique_address]] */ _Delim _Delimiter; - - template - _NODISCARD constexpr auto operator()(_Rng&& _Range) const& noexcept( - noexcept(lazy_split_view(_STD forward<_Rng>(_Range), _Delimiter))) requires requires { - lazy_split_view(static_cast<_Rng&&>(_Range), _Delimiter); - } - { return lazy_split_view(_STD forward<_Rng>(_Range), _Delimiter); } - - template - _NODISCARD constexpr auto operator()(_Rng&& _Range) && noexcept(noexcept( - lazy_split_view(_STD forward<_Rng>(_Range), _STD forward<_Delim>(_Delimiter)))) requires requires { - lazy_split_view(static_cast<_Rng&&>(_Range), static_cast<_Delim&&>(_Delimiter)); - } - { return lazy_split_view(_STD forward<_Rng>(_Range), _STD forward<_Delim>(_Delimiter)); } - }; - - public: + struct _Lazy_split_fn { // clang-format off template _NODISCARD constexpr auto operator()(_Rng&& _Range, _Pat&& _Pattern) const noexcept(noexcept( @@ -3806,11 +3693,11 @@ namespace ranges { // clang-format off template - requires is_lvalue_reference_v<_Delim> || move_constructible<_Delim> + requires constructible_from, _Delim> _NODISCARD constexpr auto operator()(_Delim&& _Delimiter) const - noexcept(is_lvalue_reference_v<_Delim> || is_nothrow_move_constructible_v<_Delim>) { + noexcept(is_nothrow_constructible_v, _Delim>) { // clang-format on - return _Partial<_Delim>{._Delimiter = _STD forward<_Delim>(_Delimiter)}; + return _Range_closure<_Lazy_split_fn, decay_t<_Delim>>{_STD forward<_Delim>(_Delimiter)}; } }; @@ -3982,28 +3869,7 @@ namespace ranges { split_view(_Rng&&, range_value_t<_Rng>) -> split_view, single_view>>; namespace views { - class _Split_fn { - private: - template - struct _Partial : _Pipe::_Base<_Partial<_Delim>> { - /* [[no_unique_address]] */ _Delim _Delimiter; - - template - _NODISCARD constexpr auto operator()(_Rng&& _Range) const& noexcept( - noexcept(split_view(_STD forward<_Rng>(_Range), _Delimiter))) requires requires { - split_view(static_cast<_Rng&&>(_Range), _Delimiter); - } - { return split_view(_STD forward<_Rng>(_Range), _Delimiter); } - - template - _NODISCARD constexpr auto operator()(_Rng&& _Range) && noexcept(noexcept( - split_view(_STD forward<_Rng>(_Range), _STD forward<_Delim>(_Delimiter)))) requires requires { - split_view(static_cast<_Rng&&>(_Range), static_cast<_Delim&&>(_Delimiter)); - } - { return split_view(_STD forward<_Rng>(_Range), _STD forward<_Delim>(_Delimiter)); } - }; - - public: + struct _Split_fn { // clang-format off template _NODISCARD constexpr auto operator()(_Rng&& _Range, _Pat&& _Pattern) const noexcept(noexcept( @@ -4016,11 +3882,11 @@ namespace ranges { // clang-format off template - requires is_lvalue_reference_v<_Delim> || move_constructible<_Delim> + requires constructible_from, _Delim> _NODISCARD constexpr auto operator()(_Delim&& _Delimiter) const - noexcept(is_lvalue_reference_v<_Delim> || is_nothrow_move_constructible_v<_Delim>) { + noexcept(is_nothrow_constructible_v, _Delim>) { // clang-format on - return _Partial<_Delim>{._Delimiter = _STD forward<_Delim>(_Delimiter)}; + return _Range_closure<_Split_fn, decay_t<_Delim>>{_STD forward<_Delim>(_Delimiter)}; } }; diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index 4d245fd85a..a50241bf19 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -255,6 +255,7 @@ // P2210R2 Superior String Splitting // P2231R1 Completing constexpr In optional And variant // P2259R1 Repairing Input Range Adaptors And counted_iterator +// P2281R1 Clarifying Range Adaptor Objects // P2325R3 Views Should Not Be Required To Be Default Constructible // P2328R1 join_view Should Join All views Of ranges // P2367R0 Remove Misuses Of List-Initialization From Clause 24 Ranges diff --git a/tests/std/tests/P0896R4_ranges_range_machinery/test.cpp b/tests/std/tests/P0896R4_ranges_range_machinery/test.cpp index 12c68e5d24..fa3129cfd3 100644 --- a/tests/std/tests/P0896R4_ranges_range_machinery/test.cpp +++ b/tests/std/tests/P0896R4_ranges_range_machinery/test.cpp @@ -1886,6 +1886,48 @@ namespace unwrapped_begin_end { } } // namespace unwrapped_begin_end +namespace closure { + // Verify that range adaptor closure capture with the proper value category + + enum class path { none, lvalue, const_lvalue, rvalue, const_rvalue }; + + template + struct arg { + constexpr arg(arg&) { + static_assert(allowed == path::lvalue); + } + constexpr arg(const arg&) { + static_assert(allowed == path::const_lvalue); + } + constexpr arg(arg&&) { + static_assert(allowed == path::rvalue); + } + constexpr arg(const arg&&) { + static_assert(allowed == path::const_rvalue); + } + + private: + friend void test(); + arg() = default; + }; + + void test() { + using std::as_const, std::move, std::views::filter; + + arg l; + (void) filter(l); + + arg cl; + (void) filter(as_const(cl)); + + arg r; + (void) filter(move(r)); + + arg cr; + (void) filter(move(as_const(cr))); + } +} // namespace closure + int main() { // Validate conditional constexpr STATIC_ASSERT(test_array_ish>()); @@ -1903,4 +1945,6 @@ int main() { STATIC_ASSERT(unwrapped_begin_end::test()); unwrapped_begin_end::test(); + + closure::test(); } diff --git a/tests/std/tests/P0896R4_views_join/test.cpp b/tests/std/tests/P0896R4_views_join/test.cpp index b6cc29fc3f..9338a8f500 100644 --- a/tests/std/tests/P0896R4_views_join/test.cpp +++ b/tests/std/tests/P0896R4_views_join/test.cpp @@ -524,7 +524,7 @@ int main() { { // P2328 range of prvalue array static constexpr int result[] = {1, 2, 3, 4, 5}; - auto ToArray = [](const int i) { return array{i + 1}; }; + constexpr auto ToArray = [](const int i) { return array{i + 1}; }; assert(ranges::equal(views::iota(0, 5) | views::transform(ToArray) | views::join, result)); static_assert(ranges::equal(views::iota(0, 5) | views::transform(ToArray) | views::join, result)); } @@ -562,7 +562,7 @@ int main() { } { // Immovable type - auto ToArrayOfImmovable = [](int) { return array{}; }; + constexpr auto ToArrayOfImmovable = [](int) { return array{}; }; assert(ranges::distance(views::iota(0, 2) | views::transform(ToArrayOfImmovable) | views::join) == 6); static_assert(ranges::distance(views::iota(0, 2) | views::transform(ToArrayOfImmovable) | views::join) == 6); } From 66c1aaa624909066188468a190ad3b153dafd0c1 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Mon, 20 Sep 2021 13:53:32 -0700 Subject: [PATCH 2/4] STL's review comments --- stl/inc/ranges | 51 +++++++++++-------- .../P0896R4_ranges_range_machinery/test.cpp | 22 ++++---- 2 files changed, 41 insertions(+), 32 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 1b8da3a6ce..a12a151211 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -780,55 +780,64 @@ namespace ranges { template class _Range_closure : public _Pipe::_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 qualifers, so we can use `invocable<_Fn, ` without + // loss of generality, and + // 2. _Fn must be default-constructible and stateless, so we can create instances "on-the-fly" and avoid + // storing a copy. + _STL_INTERNAL_STATIC_ASSERT((same_as, _Types> && ...)); _STL_INTERNAL_STATIC_ASSERT(is_empty_v<_Fn>&& is_default_constructible_v<_Fn>); + // clang-format off template + requires (same_as, _Types> && ...) constexpr explicit _Range_closure(_UTypes&&... _Args) noexcept( - (is_nothrow_constructible_v<_Types, _UTypes> && ...)) - : _Captures(_STD forward<_UTypes>(_Args)...) { - _STL_INTERNAL_STATIC_ASSERT((same_as, _Types> && ...)); - } + conjunction_v...>) + : _Captures(_STD forward<_UTypes>(_Args)...) {} + // clang-format on void operator()(auto&&) & = delete; void operator()(auto&&) const& = delete; void operator()(auto&&) && = delete; void operator()(auto&&) const&& = delete; + using _Indices = index_sequence_for<_Types...>; + template - requires invocable + requires invocable<_Fn, _Ty, _Types&...> constexpr decltype(auto) operator()(_Ty&& _Arg) & noexcept( - noexcept(_Call(_STD forward<_Ty>(_Arg), index_sequence_for<_Types...>{}, _Captures))) { - return _Call(_STD forward<_Ty>(_Arg), index_sequence_for<_Types...>{}, _Captures); + noexcept(_Call(*this, _STD forward<_Ty>(_Arg), _Indices{}))) { + return _Call(*this, _STD forward<_Ty>(_Arg), _Indices{}); } template - requires invocable + requires invocable<_Fn, _Ty, const _Types&...> constexpr decltype(auto) operator()(_Ty&& _Arg) const& noexcept( - noexcept(_Call(_STD forward<_Ty>(_Arg), index_sequence_for<_Types...>{}, _Captures))) { - return _Call(_STD forward<_Ty>(_Arg), index_sequence_for<_Types...>{}, _Captures); + noexcept(_Call(*this, _STD forward<_Ty>(_Arg), _Indices{}))) { + return _Call(*this, _STD forward<_Ty>(_Arg), _Indices{}); } template - requires invocable + requires invocable<_Fn, _Ty, _Types...> constexpr decltype(auto) operator()(_Ty&& _Arg) && noexcept( - noexcept(_Call(_STD forward<_Ty>(_Arg), index_sequence_for<_Types...>{}, _STD move(_Captures)))) { - return _Call(_STD forward<_Ty>(_Arg), index_sequence_for<_Types...>{}, _STD move(_Captures)); + noexcept(_Call(_STD move(*this), _STD forward<_Ty>(_Arg), _Indices{}))) { + return _Call(_STD move(*this), _STD forward<_Ty>(_Arg), _Indices{}); } template - requires invocable + requires invocable<_Fn, _Ty, const _Types...> constexpr decltype(auto) operator()(_Ty&& _Arg) const&& noexcept( - noexcept(_Call(_STD forward<_Ty>(_Arg), index_sequence_for<_Types...>{}, _STD move(_Captures)))) { - return _Call(_STD forward<_Ty>(_Arg), index_sequence_for<_Types...>{}, _STD move(_Captures)); + noexcept(_Call(_STD move(*this), _STD forward<_Ty>(_Arg), _Indices{}))) { + return _Call(_STD move(*this), _STD forward<_Ty>(_Arg), _Indices{}); } private: - template - static constexpr decltype(auto) _Call(_Ty&& _Arg, index_sequence<_Idx...>, _Tuple&& _Captures) noexcept( - noexcept(_Fn{}(_STD forward<_Ty>(_Arg), _STD get<_Idx>(_STD forward<_Tuple>(_Captures))...))) { - _STL_INTERNAL_STATIC_ASSERT(same_as, index_sequence_for<_Types...>>); - return _Fn{}(_STD forward<_Ty>(_Arg), _STD get<_Idx>(_STD forward<_Tuple>(_Captures))...); + template + static constexpr decltype(auto) _Call(_SelfTy&& _Self, _Ty&& _Arg, index_sequence<_Idx...>) noexcept( + noexcept(_Fn{}(_STD forward<_Ty>(_Arg), _STD get<_Idx>(_STD forward<_SelfTy>(_Self)._Captures)...))) { + _STL_INTERNAL_STATIC_ASSERT(same_as, _Indices>); + return _Fn{}(_STD forward<_Ty>(_Arg), _STD get<_Idx>(_STD forward<_SelfTy>(_Self)._Captures)...); } tuple<_Types...> _Captures; diff --git a/tests/std/tests/P0896R4_ranges_range_machinery/test.cpp b/tests/std/tests/P0896R4_ranges_range_machinery/test.cpp index fa3129cfd3..d43416880a 100644 --- a/tests/std/tests/P0896R4_ranges_range_machinery/test.cpp +++ b/tests/std/tests/P0896R4_ranges_range_machinery/test.cpp @@ -1887,23 +1887,23 @@ namespace unwrapped_begin_end { } // namespace unwrapped_begin_end namespace closure { - // Verify that range adaptor closure capture with the proper value category + // Verify that range adaptor closures capture with the proper value category - enum class path { none, lvalue, const_lvalue, rvalue, const_rvalue }; + enum class GLValueKind { lvalue, const_lvalue, xvalue, const_xvalue }; - template + template struct arg { constexpr arg(arg&) { - static_assert(allowed == path::lvalue); + static_assert(Allowed == GLValueKind::lvalue); } constexpr arg(const arg&) { - static_assert(allowed == path::const_lvalue); + static_assert(Allowed == GLValueKind::const_lvalue); } constexpr arg(arg&&) { - static_assert(allowed == path::rvalue); + static_assert(Allowed == GLValueKind::xvalue); } constexpr arg(const arg&&) { - static_assert(allowed == path::const_rvalue); + static_assert(Allowed == GLValueKind::const_xvalue); } private: @@ -1914,16 +1914,16 @@ namespace closure { void test() { using std::as_const, std::move, std::views::filter; - arg l; + arg l; (void) filter(l); - arg cl; + arg cl; (void) filter(as_const(cl)); - arg r; + arg r; (void) filter(move(r)); - arg cr; + arg cr; (void) filter(move(as_const(cr))); } } // namespace closure From a60bd998d85bf1815c4c0651c734075f9d3f78bf Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Tue, 21 Sep 2021 20:15:38 -0700 Subject: [PATCH 3/4] Fix comment typos. --- stl/inc/ranges | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index a12a151211..669adc5d28 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -780,8 +780,8 @@ namespace ranges { template class _Range_closure : public _Pipe::_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 qualifers, so we can use `invocable<_Fn, ` without + // 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 // loss of generality, and // 2. _Fn must be default-constructible and stateless, so we can create instances "on-the-fly" and avoid // storing a copy. From 20586143e9f464fee08aa46781a2322254350707 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Thu, 23 Sep 2021 14:31:38 -0700 Subject: [PATCH 4/4] Work around a compiler bug. --- tests/std/tests/P1502R1_standard_library_header_units/test.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/std/tests/P1502R1_standard_library_header_units/test.cpp b/tests/std/tests/P1502R1_standard_library_header_units/test.cpp index 467b119041..3df0310656 100644 --- a/tests/std/tests/P1502R1_standard_library_header_units/test.cpp +++ b/tests/std/tests/P1502R1_standard_library_header_units/test.cpp @@ -576,12 +576,14 @@ int main() { assert(lcg() == 1043618065); // N4868 [rand.predef]/1 } +#ifndef MSVC_INTERNAL_TESTING // TRANSITION, VSO-1409853 (internal compiler assertion, doesn't affect public releases) { puts("Testing ."); constexpr int arr[]{11, 0, 22, 0, 33, 0, 44, 0, 55}; assert(ranges::distance(views::filter(arr, [](int x) { return x == 0; })) == 4); static_assert(ranges::distance(views::filter(arr, [](int x) { return x != 0; })) == 5); } +#endif // ^^^ no workaround ^^^ { puts("Testing .");