From 56a65e0471f73b234784046ca09ae0ed3d86c97f Mon Sep 17 00:00:00 2001 From: Jakub Mazurkiewicz Date: Fri, 17 Mar 2023 03:42:16 +0100 Subject: [PATCH] P2321R2: `views::adjacent_transform`, `views::pairwise_transform` (#3546) Co-authored-by: Stephan T. Lavavej --- stl/inc/ranges | 394 ++++++- stl/inc/yvals_core.h | 2 +- tests/libcxx/expected_results.txt | 4 - tests/std/test.lst | 1 + .../std/tests/P2321R2_views_adjacent/test.cpp | 28 +- .../P2321R2_views_adjacent_transform/env.lst | 4 + .../P2321R2_views_adjacent_transform/test.cpp | 973 ++++++++++++++++++ .../test.compile.pass.cpp | 14 + 8 files changed, 1393 insertions(+), 27 deletions(-) create mode 100644 tests/std/tests/P2321R2_views_adjacent_transform/env.lst create mode 100644 tests/std/tests/P2321R2_views_adjacent_transform/test.cpp diff --git a/stl/inc/ranges b/stl/inc/ranges index cf7d9e016b..ab03fe9702 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -8778,20 +8778,47 @@ namespace ranges { _EXPORT_STD inline constexpr _Zip_transform_fn zip_transform{}; } // namespace views + template + using _Repeat_type = _Ty; + template struct _Repeated_tuple_impl; template struct _Repeated_tuple_impl<_Ty, index_sequence<_Indices...>> { - template - using _Repeat_type = _Ty; - - using type = tuple<_Repeat_type<_Indices>...>; + using type = tuple<_Repeat_type<_Ty, _Indices>...>; }; template using _Repeated_tuple = typename _Repeated_tuple_impl<_Ty, make_index_sequence<_Nx>>::type; + template + inline constexpr bool _Regular_invocable_with_repeated_type_impl = false; + + template + inline constexpr bool _Regular_invocable_with_repeated_type_impl<_Fn, _Ty, index_sequence<_Indices...>> = + regular_invocable<_Fn, _Repeat_type<_Ty, _Indices>...>; + + template + concept _Regular_invocable_with_repeated_type = + _Regular_invocable_with_repeated_type_impl<_Fn, _Ty, make_index_sequence<_Nx>>; + + template + struct _Invoke_result_with_repeated_type_impl; + + template + struct _Invoke_result_with_repeated_type_impl<_Fn, _Ty, index_sequence<_Indices...>> { +#if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-1761088 + using type = invoke_result_t<_Fn, _Repeat_type<_Ty, _Indices>...>; +#else // ^^^ no workaround / workaround vvv + using type = decltype(_STD invoke(_STD declval<_Fn>(), _STD declval<_Repeat_type<_Ty, _Indices>>()...)); +#endif // ^^^ workaround ^^^ + }; + + template + using _Invoke_result_with_repeated_type = + typename _Invoke_result_with_repeated_type_impl<_Fn, _Ty, make_index_sequence<_Nx>>::type; + _EXPORT_STD template requires view<_Vw> && (_Nx > 0) class adjacent_view : public view_interface> { @@ -8807,6 +8834,12 @@ namespace ranges { private: friend adjacent_view; + template + requires view<_Vw2> && (_Nx2 > 0) + && is_object_v<_Fn> && _Regular_invocable_with_repeated_type<_Fn&, range_reference_t<_Vw2>, _Nx2> + && _Can_reference<_Invoke_result_with_repeated_type<_Fn&, range_reference_t<_Vw2>, _Nx2>> + friend class adjacent_transform_view; + using _Base = _Maybe_const<_Const, _Vw>; using _Base_iterator = iterator_t<_Base>; @@ -9167,6 +9200,359 @@ namespace ranges { _EXPORT_STD inline constexpr _Adjacent_fn<2> pairwise; } // namespace views + _EXPORT_STD template + requires view<_Vw> && (_Nx > 0) + && is_object_v<_Fn> && _Regular_invocable_with_repeated_type<_Fn&, range_reference_t<_Vw>, _Nx> + && _Can_reference<_Invoke_result_with_repeated_type<_Fn&, range_reference_t<_Vw>, _Nx>> + class adjacent_transform_view : public view_interface> { + private: + using _Inner_view = adjacent_view<_Vw, _Nx>; + template + using _Inner_iterator = iterator_t<_Maybe_const<_Const, _Inner_view>>; + template + using _Inner_sentinel = sentinel_t<_Maybe_const<_Const, _Inner_view>>; + + /* [[no_unique_address]] */ _Movable_box<_Fn> _Func; + /* [[no_unique_address]] */ _Inner_view _Inner; + + template + class _Iterator { + private: + friend adjacent_transform_view; + + using _Parent_t = _Maybe_const<_Const, adjacent_transform_view>; + using _Base = _Maybe_const<_Const, _Vw>; + + _Parent_t* _Parent = nullptr; + _Inner_iterator<_Const> _Inner; + + constexpr _Iterator(_Parent_t& _Parent_, _Inner_iterator<_Const> _Inner_) noexcept( + is_nothrow_move_constructible_v<_Inner_iterator<_Const>>) // strengthened + : _Parent(_STD addressof(_Parent_)), _Inner(_STD move(_Inner_)) {} + + _NODISCARD static consteval auto _Get_iterator_category() noexcept { + if constexpr (!is_reference_v<_Invoke_result_with_repeated_type<_Maybe_const<_Const, _Fn>&, + range_reference_t<_Base>, _Nx>>) { + return input_iterator_tag{}; + } else { + using _Cat = typename iterator_traits>::iterator_category; + + if constexpr (derived_from<_Cat, random_access_iterator_tag>) { + return random_access_iterator_tag{}; + } else if constexpr (derived_from<_Cat, bidirectional_iterator_tag>) { + return bidirectional_iterator_tag{}; + } else if constexpr (derived_from<_Cat, forward_iterator_tag>) { + return forward_iterator_tag{}; + } else { + return input_iterator_tag{}; + } + } + } + + template + _NODISCARD static consteval bool _Is_indirection_nothrow(index_sequence<_Indices...>) noexcept { + return noexcept(_STD invoke(_STD declval<_Maybe_const<_Const, _Fn>&>(), + *_STD get<_Indices>(_STD declval&>()._Current)...)); + } + + public: + using iterator_category = decltype(_Get_iterator_category()); + using iterator_concept = typename _Inner_iterator<_Const>::iterator_concept; + using value_type = remove_cvref_t< + _Invoke_result_with_repeated_type<_Maybe_const<_Const, _Fn>&, range_reference_t<_Base>, _Nx>>; + using difference_type = range_difference_t<_Base>; + + _Iterator() = default; + + constexpr _Iterator(_Iterator _Other) + requires _Const && convertible_to<_Inner_iterator, _Inner_iterator<_Const>> + : _Parent(_Other._Parent), _Inner(_STD move(_Other._Inner)) {} + + _NODISCARD constexpr decltype(auto) operator*() const + noexcept(_Is_indirection_nothrow(make_index_sequence<_Nx>{})) { + return _STD apply( + [&](const auto&... _Iters) -> decltype(auto) { return _STD invoke(*_Parent->_Func, *_Iters...); }, + _Inner._Current); + } + + constexpr _Iterator& operator++() noexcept(noexcept(++_Inner)) /* strengthened */ { + ++_Inner; + return *this; + } + + constexpr _Iterator operator++(int) noexcept( + noexcept(++*this) && is_nothrow_copy_constructible_v<_Iterator>) /* strengthened */ { + auto _Tmp = *this; + ++*this; + return _Tmp; + } + + constexpr _Iterator& operator--() noexcept(noexcept(--_Inner)) /* strengthened */ + requires bidirectional_range<_Base> + { + --_Inner; + return *this; + } + + constexpr _Iterator operator--(int) noexcept( + noexcept(--*this) && is_nothrow_copy_constructible_v<_Iterator>) // strengthened + requires bidirectional_range<_Base> + { + auto _Tmp = *this; + --*this; + return _Tmp; + } + + constexpr _Iterator& operator+=(const difference_type _Off) noexcept( + noexcept(_Inner += _Off)) // strengthened + requires random_access_range<_Base> + { + _Inner += _Off; + return *this; + } + + constexpr _Iterator& operator-=(const difference_type _Off) noexcept( + noexcept(_Inner -= _Off)) // strengthened + requires random_access_range<_Base> + { + _Inner -= _Off; + return *this; + } + + _NODISCARD constexpr decltype(auto) operator[](const difference_type _Off) const + requires random_access_range<_Base> + { + return _STD apply( + [&](const auto&... _Iters) -> decltype(auto) { + return _STD invoke(*_Parent->_Func, _Iters[_Off]...); + }, + _Inner._Current); + } + + _NODISCARD_FRIEND constexpr bool operator==(const _Iterator& _Left, const _Iterator& _Right) noexcept( + noexcept(_Left._Inner == _Right._Inner)) /* strengthened */ { + return _Left._Inner == _Right._Inner; + } + + _NODISCARD_FRIEND constexpr bool operator<(const _Iterator& _Left, const _Iterator& _Right) noexcept( + noexcept(_Left._Inner < _Right._Inner)) // strengthened + requires random_access_range<_Base> + { + return _Left._Inner < _Right._Inner; + } + + _NODISCARD_FRIEND constexpr bool operator>(const _Iterator& _Left, const _Iterator& _Right) noexcept( + noexcept(_Left._Inner > _Right._Inner)) // strengthened + requires random_access_range<_Base> + { + return _Left._Inner > _Right._Inner; + } + + _NODISCARD_FRIEND constexpr bool operator<=(const _Iterator& _Left, const _Iterator& _Right) noexcept( + noexcept(_Left._Inner <= _Right._Inner)) // strengthened + requires random_access_range<_Base> + { + return _Left._Inner <= _Right._Inner; + } + + _NODISCARD_FRIEND constexpr bool operator>=(const _Iterator& _Left, const _Iterator& _Right) noexcept( + noexcept(_Left._Inner >= _Right._Inner)) // strengthened + requires random_access_range<_Base> + { + return _Left._Inner >= _Right._Inner; + } + + _NODISCARD_FRIEND constexpr auto operator<=>(const _Iterator& _Left, const _Iterator& _Right) noexcept( + noexcept(_Left._Inner <=> _Right._Inner)) // strengthened + requires random_access_range<_Base> && three_way_comparable<_Inner_iterator<_Const>> + { + return _Left._Inner <=> _Right._Inner; + } + + _NODISCARD_FRIEND constexpr _Iterator operator+(const _Iterator& _It, const difference_type _Off) noexcept( + noexcept(_It._Inner + _Off) && is_nothrow_move_constructible_v<_Inner_iterator<_Const>>) // strengthened + requires random_access_range<_Base> + { + return _Iterator{*_It._Parent, _It._Inner + _Off}; + } + + _NODISCARD_FRIEND constexpr _Iterator operator+(const difference_type _Off, const _Iterator& _It) noexcept( + noexcept(_It._Inner + _Off) && is_nothrow_move_constructible_v<_Inner_iterator<_Const>>) // strengthened + requires random_access_range<_Base> + { + return _Iterator{*_It._Parent, _It._Inner + _Off}; + } + + _NODISCARD_FRIEND constexpr _Iterator operator-(const _Iterator& _It, const difference_type _Off) noexcept( + noexcept(_It._Inner - _Off) && is_nothrow_move_constructible_v<_Inner_iterator<_Const>>) // strengthened + requires random_access_range<_Base> + { + return _Iterator{*_It._Parent, _It._Inner - _Off}; + } + + _NODISCARD_FRIEND constexpr difference_type operator-(const _Iterator& _Left, const _Iterator& _Right) // + noexcept(noexcept(_Left._Inner - _Right._Inner)) // strengthened + requires sized_sentinel_for<_Inner_iterator<_Const>, _Inner_iterator<_Const>> + { + return _Left._Inner - _Right._Inner; + } + }; + + template + class _Sentinel { + private: + friend adjacent_transform_view; + + /* [[no_unique_address]] */ _Inner_sentinel<_Const> _Inner; + + constexpr explicit _Sentinel(_Inner_sentinel<_Const> _Inner_) : _Inner(_Inner_) {} + + template + requires sentinel_for<_Inner_sentinel<_Const>, _Inner_iterator<_OtherConst>> + _NODISCARD constexpr bool _Equal(const _Iterator<_OtherConst>& _It) const + noexcept(noexcept(_It._Inner == _Inner)) { + return _It._Inner == _Inner; + } + + template + requires sized_sentinel_for<_Inner_sentinel<_Const>, _Inner_iterator<_OtherConst>> + _NODISCARD constexpr range_difference_t<_Maybe_const<_OtherConst, _Inner_view>> _Distance_from( + const _Iterator<_OtherConst>& _It) const noexcept(noexcept(_Inner - _It._Inner)) { + return _Inner - _It._Inner; + } + + public: + _Sentinel() = default; + + constexpr _Sentinel(_Sentinel _Other) + requires _Const && convertible_to<_Inner_sentinel, _Inner_sentinel<_Const>> + : _Inner(_STD move(_Other._Inner)) {} + + template + requires sentinel_for<_Inner_sentinel<_Const>, _Inner_iterator<_OtherConst>> + _NODISCARD_FRIEND constexpr bool operator==(const _Iterator<_OtherConst>& _It, + const _Sentinel& _Se) noexcept(noexcept(_Se._Equal(_It))) /* strengthened */ { + return _Se._Equal(_It); + } + + template + requires sized_sentinel_for<_Inner_sentinel<_Const>, _Inner_iterator<_OtherConst>> + _NODISCARD_FRIEND constexpr range_difference_t<_Maybe_const<_OtherConst, _Inner_view>> operator-( + const _Iterator<_OtherConst>& _It, const _Sentinel& _Se) noexcept( // + noexcept(_Se._Distance_from(_It))) /* strengthened */ { + return -_Se._Distance_from(_It); + } + + template + requires sized_sentinel_for<_Inner_sentinel<_Const>, _Inner_iterator<_OtherConst>> + _NODISCARD_FRIEND constexpr range_difference_t<_Maybe_const<_OtherConst, _Inner_view>> operator-( + const _Sentinel& _Se, const _Iterator<_OtherConst>& _It) noexcept( // + noexcept(_Se._Distance_from(_It))) /* strengthened */ { + return _Se._Distance_from(_It); + } + }; + + public: + adjacent_transform_view() = default; + + constexpr explicit adjacent_transform_view(_Vw _Range_, _Fn _Func_) noexcept( + is_nothrow_move_constructible_v<_Vw>&& is_nothrow_move_constructible_v<_Fn>) // strengthened + : _Func(in_place, _STD move(_Func_)), _Inner(_STD move(_Range_)) {} + + _NODISCARD constexpr _Vw base() const& noexcept(noexcept(_Inner.base())) // strengthened + requires copy_constructible<_Inner_view> + { + return _Inner.base(); + } + + _NODISCARD constexpr _Vw base() && noexcept(noexcept(_STD move(_Inner).base())) /* strengthened */ { + return _STD move(_Inner).base(); + } + + _NODISCARD constexpr auto begin() { + return _Iterator{*this, _Inner.begin()}; + } + + _NODISCARD constexpr auto begin() const + requires range + && _Regular_invocable_with_repeated_type, _Nx> + { + return _Iterator{*this, _Inner.begin()}; + } + + _NODISCARD constexpr auto end() { + if constexpr (common_range<_Inner_view>) { + return _Iterator{*this, _Inner.end()}; + } else { + return _Sentinel{_Inner.end()}; + } + } + + _NODISCARD constexpr auto end() const + requires range + && _Regular_invocable_with_repeated_type, _Nx> + { + if constexpr (common_range) { + return _Iterator{*this, _Inner.end()}; + } else { + return _Sentinel{_Inner.end()}; + } + } + + _NODISCARD constexpr auto size() noexcept(noexcept(_Inner.size())) // strengthened + requires sized_range<_Inner_view> + { + return _Inner.size(); + } + + _NODISCARD constexpr auto size() const noexcept(noexcept(_Inner.size())) // strengthened + requires sized_range + { + return _Inner.size(); + } + }; + + namespace views { + template + class _Adjacent_transform_fn { + public: + template + _NODISCARD constexpr auto operator()(_Rng&&, _Fn&& _Func) const + noexcept(noexcept(views::zip_transform(_STD forward<_Fn>(_Func)))) + requires (_Nx == 0) +#ifndef __clang__ // TRANSITION, Clang 16 + && requires { views::zip_transform(_STD forward<_Fn>(_Func)); } +#endif // __clang__ + { + return views::zip_transform(_STD forward<_Fn>(_Func)); + } + + template + _NODISCARD constexpr auto operator()(_Rng&& _Range, _Fn&& _Func) const + noexcept(noexcept(adjacent_transform_view, decay_t<_Fn>, _Nx>( + _STD forward<_Rng>(_Range), _STD forward<_Fn>(_Func)))) + requires requires { + adjacent_transform_view, decay_t<_Fn>, _Nx>( + _STD forward<_Rng>(_Range), _STD forward<_Fn>(_Func)); + } + { + return adjacent_transform_view, decay_t<_Fn>, _Nx>( + _STD forward<_Rng>(_Range), _STD forward<_Fn>(_Func)); + } + + template + requires constructible_from, _Fn> + _NODISCARD constexpr auto operator()(_Fn&& _Func) const + noexcept(is_nothrow_constructible_v, _Fn>) { + return _Range_closure<_Adjacent_transform_fn, decay_t<_Fn>>{_STD forward<_Fn>(_Func)}; + } + }; + + _EXPORT_STD template + inline constexpr _Adjacent_transform_fn<_Nx> adjacent_transform; + _EXPORT_STD inline constexpr _Adjacent_transform_fn<2> pairwise_transform; + } // namespace views + #ifdef __cpp_lib_ranges_to_container // clang-format off template diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index 1a4168eba2..03eeca0afb 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -335,7 +335,6 @@ // P2291R3 constexpr Integral // P2302R4 ranges::contains, ranges::contains_subrange // P2321R2 zip -// (missing views::adjacent_transform) // P2322R6 ranges::fold_left, ranges::fold_right, Etc. // P2387R3 Pipe Support For User-Defined Range Adaptors // P2404R3 Move-Only Types For Comparison Concepts @@ -1739,6 +1738,7 @@ _EMIT_STL_ERROR(STL1004, "C++98 unexpected() is incompatible with C++23 unexpect #define __cpp_lib_ranges_starts_ends_with 202106L #define __cpp_lib_ranges_stride 202207L #define __cpp_lib_ranges_to_container 202202L +#define __cpp_lib_ranges_zip 202110L #endif // __cpp_lib_concepts #define __cpp_lib_spanstream 202106L diff --git a/tests/libcxx/expected_results.txt b/tests/libcxx/expected_results.txt index 88a05cc2ad..2d41f7f11b 100644 --- a/tests/libcxx/expected_results.txt +++ b/tests/libcxx/expected_results.txt @@ -311,10 +311,6 @@ std/utilities/format/format.tuple/parse.pass.cpp FAIL std/utilities/format/format.tuple/set_brackets.pass.cpp FAIL std/utilities/format/format.tuple/set_separator.pass.cpp FAIL -# P2321R2 zip -std/language.support/support.limits/support.limits.general/tuple.version.compile.pass.cpp FAIL -std/language.support/support.limits/support.limits.general/utility.version.compile.pass.cpp FAIL - # *** MISSING COMPILER FEATURES *** # MSVC doesn't properly support [[no_unique_address]] diff --git a/tests/std/test.lst b/tests/std/test.lst index fc8574d240..f15f54508b 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -569,6 +569,7 @@ tests\P2302R4_ranges_alg_contains tests\P2302R4_ranges_alg_contains_subrange tests\P2321R2_proxy_reference tests\P2321R2_views_adjacent +tests\P2321R2_views_adjacent_transform tests\P2321R2_views_zip tests\P2321R2_views_zip_transform tests\P2322R6_ranges_alg_fold diff --git a/tests/std/tests/P2321R2_views_adjacent/test.cpp b/tests/std/tests/P2321R2_views_adjacent/test.cpp index bc993cf73b..bc8b9693fc 100644 --- a/tests/std/tests/P2321R2_views_adjacent/test.cpp +++ b/tests/std/tests/P2321R2_views_adjacent/test.cpp @@ -142,19 +142,15 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { // Check view_interface::empty and operator bool STATIC_ASSERT(CanMemberEmpty); - STATIC_ASSERT(CanBool == CanEmpty); - if constexpr (CanMemberEmpty) { - assert(r.empty() == is_empty); - assert(static_cast(r) == !is_empty); - } + STATIC_ASSERT(CanBool); + assert(r.empty() == is_empty); + assert(static_cast(r) == !is_empty); // Check view_interface::empty and operator bool (const) STATIC_ASSERT(CanMemberEmpty); - STATIC_ASSERT(CanBool == CanEmpty); - if constexpr (CanMemberEmpty) { - assert(as_const(r).empty() == is_empty); - assert(static_cast(as_const(r)) == !is_empty); - } + STATIC_ASSERT(CanBool); + assert(as_const(r).empty() == is_empty); + assert(static_cast(as_const(r)) == !is_empty); // Check content assert(ranges::equal(r, expected)); @@ -306,16 +302,12 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { } // Check view_interface::front - STATIC_ASSERT(CanMemberFront == forward_range); - if constexpr (CanMemberFront) { - assert(r.front() == *begin(expected)); - } + STATIC_ASSERT(CanMemberFront); + assert(r.front() == *begin(expected)); // Check view_interface::front (const) - STATIC_ASSERT(CanMemberFront == forward_range); - if constexpr (CanMemberFront) { - assert(as_const(r).front() == *begin(expected)); - } + STATIC_ASSERT(CanMemberFront); + assert(as_const(r).front() == *begin(expected)); // Check view_interface::back STATIC_ASSERT(CanMemberBack == (bidirectional_range && common_range) ); diff --git a/tests/std/tests/P2321R2_views_adjacent_transform/env.lst b/tests/std/tests/P2321R2_views_adjacent_transform/env.lst new file mode 100644 index 0000000000..8ac7033b20 --- /dev/null +++ b/tests/std/tests/P2321R2_views_adjacent_transform/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\strict_concepts_latest_matrix.lst diff --git a/tests/std/tests/P2321R2_views_adjacent_transform/test.cpp b/tests/std/tests/P2321R2_views_adjacent_transform/test.cpp new file mode 100644 index 0000000000..f5aa39fc49 --- /dev/null +++ b/tests/std/tests/P2321R2_views_adjacent_transform/test.cpp @@ -0,0 +1,973 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace std; + +template +concept CanViewAdjacentTransform = + requires(R&& r, F&& f) { views::adjacent_transform(forward(r), forward(f)); }; + +template +struct repeated_tuple_impl; + +template +struct repeated_tuple_impl> { + template + using repeat_type = T; + + using type = tuple...>; +}; + +template +using repeated_tuple = typename repeated_tuple_impl>::type; + +STATIC_ASSERT(same_as, tuple<>>); +STATIC_ASSERT(same_as, tuple>); +STATIC_ASSERT(same_as, tuple>); + +template +struct regular_invocable_with_repeated_type_impl; + +template +struct regular_invocable_with_repeated_type_impl> { + template + using repeat_type = T; + + static constexpr bool value = regular_invocable...>; +}; + +template +concept regular_invocable_with_repeated_type = + regular_invocable_with_repeated_type_impl>::value; + +static_assert(regular_invocable_with_repeated_type, int, 2>); +static_assert(!regular_invocable_with_repeated_type, int, 3>); +static_assert(!regular_invocable_with_repeated_type, span, 2>); + +template +struct invoke_result_with_repeated_type_impl : invoke_result_with_repeated_type_impl { +}; + +template +struct invoke_result_with_repeated_type_impl { + using type = invoke_result_t; +}; + +template +using invoke_result_with_repeated_type = typename invoke_result_with_repeated_type_impl::type; + +static_assert(same_as, int, 2>, int>); +static_assert(same_as, int, 2>, bool>); + +template +constexpr bool test_one(Rng&& rng, Fn func, Expected&& expected_rng) { + using ranges::adjacent_transform_view, ranges::adjacent_view, ranges::forward_range, ranges::bidirectional_range, + ranges::random_access_range, ranges::sized_range, ranges::common_range, ranges::iterator_t, + ranges::const_iterator_t, ranges::sentinel_t, ranges::const_sentinel_t, ranges::range_reference_t, + ranges::range_difference_t, ranges::begin, ranges::end; + + constexpr bool is_view = ranges::view>; + + using V = views::all_t; + using R = adjacent_transform_view, N>; + + STATIC_ASSERT(ranges::view); + STATIC_ASSERT(ranges::input_range); + STATIC_ASSERT(forward_range); + STATIC_ASSERT(bidirectional_range == bidirectional_range); + STATIC_ASSERT(random_access_range == random_access_range); + STATIC_ASSERT(!ranges::contiguous_range); + + // Check default-initializability + STATIC_ASSERT(default_initializable == default_initializable); + + // Check borrowed_range + STATIC_ASSERT(!ranges::borrowed_range); + + // Check range closure object + const auto closure = views::adjacent_transform(func); + + // ... with lvalue argument + STATIC_ASSERT(CanViewAdjacentTransform == (!is_view || copy_constructible) ); + if constexpr (CanViewAdjacentTransform) { + constexpr bool is_noexcept = + !is_view || (is_nothrow_copy_constructible_v && is_nothrow_copy_constructible_v); + + STATIC_ASSERT(same_as(rng, func)), R>); + STATIC_ASSERT(noexcept(views::adjacent_transform(rng, func)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(closure(rng)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(rng | closure) == is_noexcept); + } + + // ... with const lvalue argument + STATIC_ASSERT( + CanViewAdjacentTransform&, Fn, N> == (!is_view || copy_constructible) ); + if constexpr (CanViewAdjacentTransform&, Fn, N>) { + using RC = adjacent_transform_view&>, Fn, N>; + constexpr bool is_noexcept = + !is_view || (is_nothrow_copy_constructible_v && is_nothrow_copy_constructible_v); + + STATIC_ASSERT(same_as(as_const(rng), func)), RC>); + STATIC_ASSERT(noexcept(views::adjacent_transform(as_const(rng), func)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(closure(as_const(rng))) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(as_const(rng) | closure) == is_noexcept); + } + + // ... with rvalue argument + STATIC_ASSERT( + CanViewAdjacentTransform, Fn, N> == (is_view || movable>) ); + if constexpr (CanViewAdjacentTransform, Fn, N>) { + using RS = adjacent_transform_view>, Fn, N>; + constexpr bool is_noexcept = is_nothrow_move_constructible_v && is_nothrow_copy_constructible_v; + + STATIC_ASSERT(same_as(move(rng), func)), RS>); + STATIC_ASSERT(noexcept(views::adjacent_transform(move(rng), func)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(closure(move(rng))) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(move(rng) | closure) == is_noexcept); + } + + // ... with const rvalue argument + STATIC_ASSERT( + CanViewAdjacentTransform, Fn, N> == (is_view && copy_constructible) ); + if constexpr (CanViewAdjacentTransform, Fn, N>) { + constexpr bool is_noexcept = is_nothrow_copy_constructible_v && is_nothrow_copy_constructible_v; + + STATIC_ASSERT(same_as(move(as_const(rng)), func)), R>); + STATIC_ASSERT(noexcept(views::adjacent_transform(move(as_const(rng)), func)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(closure(move(as_const(rng)))) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(move(as_const(rng)) | closure) == is_noexcept); + } + + // Check views::pairwise_transform + if constexpr (N == 2) { + using Pairwised = decltype(views::pairwise_transform(forward(rng), func)); + using Adjacented = decltype(views::adjacent_transform<2>(forward(rng), func)); + STATIC_ASSERT(same_as); + } + + R r{forward(rng), func}; + + // Check adjacent_transform_view::size + STATIC_ASSERT(CanMemberSize == sized_range); + if constexpr (CanMemberSize) { + same_as> auto s = r.size(); + assert(_To_unsigned_like(s) == ranges::size(expected_rng)); + STATIC_ASSERT(noexcept(r.size()) == noexcept(ranges::size(rng))); // strengthened + } + + // Check adjacent_transform_view::size (const) + STATIC_ASSERT(CanMemberSize == sized_range); + if constexpr (CanMemberSize) { + same_as> auto s = as_const(r).size(); + assert(_To_unsigned_like(s) == ranges::size(expected_rng)); + STATIC_ASSERT(noexcept(as_const(r).size()) == noexcept(ranges::size(as_const(rng)))); // strengthened + } + + const bool is_empty = ranges::empty(expected_rng); + + // Check view_interface::empty and operator bool + STATIC_ASSERT(CanMemberEmpty); + STATIC_ASSERT(CanBool); + assert(r.empty() == is_empty); + assert(static_cast(r) == !is_empty); + + // Check view_interface::empty and operator bool (const) + STATIC_ASSERT(CanMemberEmpty); + STATIC_ASSERT(CanBool); + assert(as_const(r).empty() == is_empty); + assert(static_cast(as_const(r)) == !is_empty); + + // Check content + assert(ranges::equal(r, expected_rng)); + + // Check adjacent_transform_view::begin + STATIC_ASSERT(CanMemberBegin); + { + const same_as> auto i = r.begin(); + if (!is_empty) { + assert(*i == *begin(expected_rng)); + } + + if constexpr (copy_constructible) { + auto r2 = r; + const same_as> auto i2 = r2.begin(); + if (!is_empty) { + assert(*i2 == *i); + } + } + } + + // Check adjacent_transform_view::begin (const) + STATIC_ASSERT( + CanMemberBegin + == (ranges::range && regular_invocable_with_repeated_type, N>) ); + if constexpr (CanMemberBegin) { + const same_as> auto ci = as_const(r).begin(); + if (!is_empty) { + assert(*ci == *begin(expected_rng)); + } + + if constexpr (copy_constructible) { + const auto cr2 = r; + const same_as> auto ci2 = cr2.begin(); + if (!is_empty) { + assert(*ci2 == *ci); + } + } + } + + // Check adjacent_transform_view::end + STATIC_ASSERT(CanMemberEnd); + { + const same_as> auto s = r.end(); + assert((r.begin() == s) == is_empty); + + if constexpr (sentinel_for, iterator_t>) { + assert((as_const(r).begin() == s) == is_empty); + } + + STATIC_ASSERT(common_range == common_range); + if constexpr (common_range && bidirectional_range) { + if (!is_empty) { + assert(*ranges::prev(s) == *ranges::prev(end(expected_rng))); + } + + if constexpr (copy_constructible) { + auto r2 = r; + if (!is_empty) { + assert(*ranges::prev(r2.end()) == *ranges::prev(end(expected_rng))); + } + } + } + } + + // Check adjacent_transform_view::end (const) + STATIC_ASSERT( + CanMemberEnd + == (ranges::range && regular_invocable_with_repeated_type, N>) ); + if constexpr (CanMemberEnd) { + const same_as> auto cs = as_const(r).end(); + assert((as_const(r).begin() == cs) == is_empty); + + if constexpr (sentinel_for, iterator_t>) { + assert((r.begin() == cs) == is_empty); + } + + STATIC_ASSERT(common_range == common_range); + if constexpr (common_range && bidirectional_range) { + if (!is_empty) { + assert(*ranges::prev(cs) == *ranges::prev(end(expected_rng))); + } + + if constexpr (copy_constructible) { + const auto r2 = r; + if (!is_empty) { + assert(*ranges::prev(r2.end()) == *ranges::prev(end(expected_rng))); + } + } + } + } + + // Check view_interface::cbegin + STATIC_ASSERT(CanMemberCBegin); + STATIC_ASSERT( + CanMemberCBegin + == (ranges::range && regular_invocable_with_repeated_type, N>) ); + { + const same_as> auto i = r.cbegin(); + if (!is_empty) { + assert(*i == *cbegin(expected_rng)); + } + + if constexpr (copyable) { + auto r2 = r; + const same_as> auto i2 = r2.cbegin(); + if (!is_empty) { + assert(*i2 == *i); + } + } + + if constexpr (CanCBegin) { + const same_as> auto i3 = as_const(r).cbegin(); + if (!is_empty) { + assert(*i3 == *i); + } + } + } + + // Check view_interface::cend + STATIC_ASSERT(CanMemberCEnd); + STATIC_ASSERT( + CanMemberCEnd + == (ranges::range && regular_invocable_with_repeated_type, N>) ); + if (!is_empty) { + same_as> auto i = r.cend(); + if constexpr (common_range && sized_range && bidirectional_range) { + assert(*ranges::prev(i) == *ranges::prev(cend(expected_rng))); + } + + if constexpr (CanCEnd) { + same_as> auto i2 = as_const(r).cend(); + if constexpr (common_range && sized_range && bidirectional_range) { + assert(*ranges::prev(i2) == *ranges::prev(cend(expected_rng))); + } + } + } + + // Check view_interface::data + STATIC_ASSERT(!CanData); + STATIC_ASSERT(!CanData); + + if (is_empty) { + return true; + } + + // Check view_interface::operator[] + STATIC_ASSERT(CanIndex == random_access_range); + if constexpr (CanIndex) { + assert(r[0] == expected_rng[0]); + } + + // Check view_interface::operator[] (const) + STATIC_ASSERT(CanIndex == random_access_range); + if constexpr (CanIndex) { + assert(as_const(r)[0] == expected_rng[0]); + } + + // Check view_interface::front + STATIC_ASSERT(CanMemberFront); + assert(r.front() == *begin(expected_rng)); + + // Check view_interface::front (const) + STATIC_ASSERT(CanMemberFront); + assert(as_const(r).front() == *begin(expected_rng)); + + // Check view_interface::back + STATIC_ASSERT(CanMemberBack == (bidirectional_range && common_range) ); + if constexpr (CanMemberBack) { + assert(r.back() == *prev(end(expected_rng))); + } + + // Check view_interface::back (const) + STATIC_ASSERT(CanMemberBack == (bidirectional_range && common_range) ); + if constexpr (CanMemberBack) { + assert(as_const(r).back() == *prev(end(expected_rng))); + } + + { // Check adjacent_transform_view::iterator + using BI = iterator_t; + using I = iterator_t; + STATIC_ASSERT(forward_iterator); + + // Check iterator_category + using IterCat = typename I::iterator_category; + if constexpr (!is_reference_v, N>>) { + STATIC_ASSERT(same_as); + } else { + using BaseCat = typename iterator_traits::iterator_category; + if constexpr (derived_from) { + STATIC_ASSERT(same_as); + } else if constexpr (derived_from) { + STATIC_ASSERT(same_as); + } else if constexpr (derived_from) { + STATIC_ASSERT(same_as); + } else { + STATIC_ASSERT(same_as); + } + } + + // Check iterator_concept + using IterConcept = typename I::iterator_concept; + STATIC_ASSERT(same_as>::iterator_concept>); + + // Check value_type + STATIC_ASSERT(same_as, N>>>); + + // Check default-initializability + STATIC_ASSERT(default_initializable); + + auto i = r.begin(); + + { // Check dereference + same_as, N>> decltype(auto) v = *as_const(i); + assert(v == expected_rng[0]); + } + + { // Check pre-incrementation + same_as decltype(auto) i2 = ++i; + assert(&i2 == &i); + if (i != r.end()) { +#pragma warning(suppress : 28020) // The expression '0<=_Param_(1)&&_Param_(1)<=1-1' is not true at this call + assert(*i == expected_rng[1]); + } + i = r.begin(); + } + + { // Check post-incrementation + same_as decltype(auto) i2 = i++; + assert(*i2 == expected_rng[0]); + if (i != r.end()) { +#pragma warning(suppress : 28020) // The expression '0<=_Param_(1)&&_Param_(1)<=1-1' is not true at this call + assert(*i == expected_rng[1]); + } + i = r.begin(); + } + + { // Check equality comparisons + auto i2 = i; + same_as auto b1 = i == i2; + assert(b1); + ++i2; + same_as auto b2 = i != i2; + assert(b2); + } + + if constexpr (bidirectional_range) { + { // Check pre-decrementation + i = ranges::next(r.begin()); + + same_as decltype(auto) i2 = --i; + assert(&i2 == &i); + assert(*i2 == expected_rng[0]); + } + + { // Check post-decrementation + i = ranges::next(r.begin()); + + same_as decltype(auto) i2 = i--; + if (i2 != r.end()) { +#pragma warning(suppress : 28020) // The expression '0<=_Param_(1)&&_Param_(1)<=1-1' is not true at this call + assert(*i2 == expected_rng[1]); + } + assert(*i == expected_rng[0]); + } + } + + if constexpr (random_access_range) { + { // Check advancement operators + same_as decltype(auto) i2 = (i += 1); + assert(&i2 == &i); + if (i != r.end()) { +#pragma warning(suppress : 28020) // The expression '0<=_Param_(1)&&_Param_(1)<=1-1' is not true at this call + assert(*i == expected_rng[1]); + } + + same_as decltype(auto) i3 = (i -= 1); + assert(&i3 == &i); + assert(*i == expected_rng[0]); + } + + { // Check subscript operator + same_as, N>> decltype(auto) v = i[0]; + assert(v == expected_rng[0]); + } + + { // Check other comparisons + auto i2 = ranges::next(i); + same_as auto b1 = i < i2; + assert(b1); + same_as auto b2 = i2 > i; + assert(b2); + same_as auto b3 = i <= i2; + assert(b3); + same_as auto b4 = i2 >= i; + assert(b4); + } + + if constexpr (three_way_comparable) { // Check 3way comparisons + using Cat = compare_three_way_result_t; + auto i2 = i; + same_as auto cmp1 = i <=> i2; + assert(cmp1 == Cat::equivalent); + ++i2; + assert((i <=> i2) == Cat::less); + assert((i2 <=> i) == Cat::greater); + } + + { // Check operator+ + same_as auto i2 = i + 1; + if (i2 != r.end()) { +#pragma warning(suppress : 28020) // The expression '0<=_Param_(1)&&_Param_(1)<=1-1' is not true at this call + assert(*i2 == expected_rng[1]); + } + + same_as auto i3 = 1 + i; + if (i3 != r.end()) { +#pragma warning(suppress : 28020) // The expression '0<=_Param_(1)&&_Param_(1)<=1-1' is not true at this call + assert(*i3 == expected_rng[1]); + } + } + + { // Check operator-(Iter, Diff) + same_as auto i2 = ranges::next(i) - 1; + assert(*i2 == expected_rng[0]); + } + } + + if constexpr (sized_sentinel_for) { // Check differencing + same_as> auto diff = i - i; + assert(diff == 0); + assert((i - ranges::next(i)) == -1); + assert((ranges::next(i) - i) == 1); + } + + if constexpr (sized_sentinel_for, I>) { // Check differencing with sentinel + const auto s = r.end(); + const auto size = ranges::ssize(expected_rng); + const same_as> auto diff1 = i - s; + assert(diff1 == -size); + const same_as> auto diff2 = s - i; + assert(diff2 == size); + } + + if constexpr (sized_sentinel_for, I>) { // Check differencing with sentinel + const auto s = as_const(r).end(); + const auto size = ranges::ssize(expected_rng); + const same_as> auto diff1 = i - s; + assert(diff1 == -size); + const same_as> auto diff2 = s - i; + assert(diff2 == size); + } + } + + // Check adjacent_transform_view::iterator + if constexpr (CanMemberBegin) { + using CBI = iterator_t; + using CI = iterator_t; + STATIC_ASSERT(forward_iterator); + + // Check iterator_category + using IterCat = typename CI::iterator_category; + if constexpr (!is_reference_v, N>>) { + STATIC_ASSERT(same_as); + } else { + using BaseCat = typename iterator_traits::iterator_category; + if constexpr (derived_from) { + STATIC_ASSERT(same_as); + } else if constexpr (derived_from) { + STATIC_ASSERT(same_as); + } else if constexpr (derived_from) { + STATIC_ASSERT(same_as); + } else { + STATIC_ASSERT(same_as); + } + } + + // Check iterator_concept + using IterConcept = typename CI::iterator_concept; + STATIC_ASSERT(same_as>::iterator_concept>); + + // Check value_type + STATIC_ASSERT(same_as, N>>>); + + // Check default-initializability + STATIC_ASSERT(default_initializable); + + iterator_t i = r.begin(); + + // Check construction from non-const iterator + CI ci = i; + + { // Check dereference + same_as, N>> decltype(auto) v = + *as_const(ci); + assert(v == expected_rng[0]); + } + + { // Check pre-incrementation + same_as decltype(auto) ci2 = ++ci; + assert(&ci2 == &ci); + if (ci != r.end()) { +#pragma warning(suppress : 28020) // The expression '0<=_Param_(1)&&_Param_(1)<=1-1' is not true at this call + assert(*ci == expected_rng[1]); + } + ci = r.begin(); + } + + { // Check post-incrementation + same_as decltype(auto) ci2 = ci++; + assert(*ci2 == expected_rng[0]); + if (ci != r.end()) { +#pragma warning(suppress : 28020) // The expression '0<=_Param_(1)&&_Param_(1)<=1-1' is not true at this call + assert(*ci == expected_rng[1]); + } + ci = r.begin(); + } + + { // Check equality comparisons + auto ci2 = ci; + same_as auto b1 = ci == ci2; + assert(b1); + ++ci2; + same_as auto b2 = ci != ci2; + assert(b2); + } + + { // Check equality comparisons with non-const iterators + same_as auto b1 = ci == i; + assert(b1); + ++i; + same_as auto b2 = ci != i; + assert(b2); + i = r.begin(); + } + + if constexpr (bidirectional_range) { + { // Check pre-decrementation + ci = ranges::next(r.begin()); + + same_as decltype(auto) ci2 = --ci; + assert(&ci2 == &ci); + assert(*ci2 == expected_rng[0]); + } + + { // Check post-decrementation + ci = ranges::next(r.begin()); + + same_as decltype(auto) ci2 = ci--; + if (ci2 != r.end()) { +#pragma warning(suppress : 28020) // The expression '0<=_Param_(1)&&_Param_(1)<=1-1' is not true at this call + assert(*ci2 == expected_rng[1]); + } + assert(*ci == expected_rng[0]); + } + } + + if constexpr (random_access_range) { + { // Check advancement operators + same_as decltype(auto) ci2 = (ci += 1); + assert(&ci2 == &ci); + if (ci != r.end()) { +#pragma warning(suppress : 28020) // The expression '0<=_Param_(1)&&_Param_(1)<=1-1' is not true at this call + assert(*ci == expected_rng[1]); + } + + same_as decltype(auto) ci3 = (ci -= 1); + assert(&ci3 == &ci); + assert(*ci == expected_rng[0]); + } + + { // Check subscript operator + same_as, N>> decltype(auto) v = + ci[0]; + assert(v == expected_rng[0]); + } + + { // Check comparisons + auto ci2 = ranges::next(ci); + same_as auto b1 = ci < ci2; + assert(b1); + same_as auto b2 = ci2 > ci; + assert(b2); + same_as auto b3 = ci <= ci2; + assert(b3); + same_as auto b4 = ci2 >= ci; + assert(b4); + } + + { // Check comparisons with non-const iterators + ++i; + same_as auto b1 = ci < i; + assert(b1); + same_as auto b2 = i > ci; + assert(b2); + same_as auto b3 = ci <= i; + assert(b3); + same_as auto b4 = i >= ci; + assert(b4); + --i; + } + + if constexpr (three_way_comparable) { // Check 3way comparisons + using Cat = compare_three_way_result_t; + auto ci2 = ci; + same_as auto cmp1 = ci <=> ci2; + assert(cmp1 == Cat::equivalent); + ++ci2; + assert((ci <=> ci2) == Cat::less); + assert((ci2 <=> ci) == Cat::greater); + } + + if constexpr (three_way_comparable) { // Check 3way comparisons with non-const iterators + using Cat = compare_three_way_result_t; + same_as auto cmp1 = ci <=> i; + assert(cmp1 == Cat::equivalent); + ++i; + assert((ci <=> i) == Cat::less); + assert((i <=> ci) == Cat::greater); + --i; + } + + { // Check operator+ + same_as auto ci2 = ci + 1; + if (ci2 != r.end()) { +#pragma warning(suppress : 28020) // The expression '0<=_Param_(1)&&_Param_(1)<=1-1' is not true at this call + assert(*ci2 == expected_rng[1]); + } + + same_as auto ci3 = 1 + ci; + if (ci3 != r.end()) { +#pragma warning(suppress : 28020) // The expression '0<=_Param_(1)&&_Param_(1)<=1-1' is not true at this call + assert(*ci3 == expected_rng[1]); + } + } + + { // Check operator-(Iter, Diff) + same_as auto ci2 = ranges::next(ci) - 1; + assert(*ci2 == expected_rng[0]); + } + } + + if constexpr (sized_sentinel_for) { // Check differencing + same_as> auto diff = ci - ci; + assert(diff == 0); + assert((ci - ranges::next(ci)) == -1); + assert((ranges::next(ci) - ci) == 1); + } + + if constexpr (sized_sentinel_for, CI>) { // Check differencing with sentinel + const auto s = r.end(); + const auto size = ranges::ssize(expected_rng); + const same_as> auto diff1 = ci - s; + assert(diff1 == -size); + const same_as> auto diff2 = s - ci; + assert(diff2 == size); + } + + if constexpr (sized_sentinel_for, CI>) { // Check differencing with sentinel + const auto s = as_const(r).end(); + const auto size = ranges::ssize(expected_rng); + const same_as> auto diff1 = ci - s; + assert(diff1 == -size); + const same_as> auto diff2 = s - ci; + assert(diff2 == size); + } + } + + // Check adjacent_transform_view::base() const& + STATIC_ASSERT(CanMemberBase == copy_constructible); + if constexpr (copy_constructible) { + [[maybe_unused]] same_as auto b1 = as_const(r).base(); + STATIC_ASSERT(noexcept(as_const(r).base()) == is_nothrow_copy_constructible_v); // strengthened + } + + // Check adjacent_transform_view::base() && + [[maybe_unused]] same_as auto b2 = move(r).base(); + STATIC_ASSERT(noexcept(move(r).base()) == is_nothrow_move_constructible_v); // strengthened + + return true; +} + +template +constexpr void test_adjacent0(Rng&& rng) { + auto func = [] { return 602; }; + using Fn = decltype(func); + + using V = views::all_t; + using E = ranges::empty_view; + + // Check range closure object + constexpr bool is_view = ranges::view>; + const auto closure = views::adjacent_transform<0>(func); + + // ... with lvalue argument + STATIC_ASSERT(CanViewAdjacentTransform == (!is_view || copy_constructible) ); + if constexpr (CanViewAdjacentTransform) { + STATIC_ASSERT(same_as(rng, func)), E>); + STATIC_ASSERT(noexcept(views::adjacent_transform<0>(rng, func)) == is_nothrow_copy_constructible_v); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(closure(rng)) == is_nothrow_copy_constructible_v); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(rng | closure) == is_nothrow_copy_constructible_v); + } + + // ... with const lvalue argument + STATIC_ASSERT( + CanViewAdjacentTransform&, Fn, 0> == (!is_view || copy_constructible) ); + if constexpr (CanViewAdjacentTransform&, Fn, 0>) { + STATIC_ASSERT(same_as(as_const(rng), func)), E>); + STATIC_ASSERT( + noexcept(views::adjacent_transform<0>(as_const(rng), func)) == is_nothrow_copy_constructible_v); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(closure(as_const(rng))) == is_nothrow_copy_constructible_v); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(as_const(rng) | closure) == is_nothrow_copy_constructible_v); + } + + // ... with rvalue argument + STATIC_ASSERT( + CanViewAdjacentTransform, Fn, 0> == (is_view || movable>) ); + if constexpr (CanViewAdjacentTransform, Fn, 0>) { + STATIC_ASSERT(same_as(move(rng), func)), E>); + STATIC_ASSERT(noexcept(views::adjacent_transform<0>(move(rng), func)) == is_nothrow_copy_constructible_v); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(closure(move(rng))) == is_nothrow_copy_constructible_v); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(move(rng) | closure) == is_nothrow_copy_constructible_v); + } + + // ... with const rvalue argument + STATIC_ASSERT( + CanViewAdjacentTransform, Fn, 0> == (is_view && copy_constructible) ); + if constexpr (CanViewAdjacentTransform, Fn, 0>) { + STATIC_ASSERT(same_as(move(as_const(rng)), func)), E>); + STATIC_ASSERT( + noexcept(views::adjacent_transform<0>(move(as_const(rng)), func)) == is_nothrow_copy_constructible_v); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(closure(move(as_const(rng)))) == is_nothrow_copy_constructible_v); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(move(as_const(rng)) | closure) == is_nothrow_copy_constructible_v); + } +} + +// We have to use std::array instead of C-array, see LLVM-61025 +constexpr auto some_ints = array{1, 3, 5, 7, 11, 13}; +constexpr auto adjacent1_fn = [](auto&& val) -> const int& { return val; }; +constexpr auto adjacent1_result = array{1, 3, 5, 7, 11, 13}; +constexpr auto pairwise_fn = plus{}; +constexpr auto pairwise_result = array{4, 8, 12, 18, 24}; +constexpr auto adjacent3_fn = [](auto... vals) { return (vals * ...); }; +constexpr auto adjacent3_result = array{15, 105, 385, 1001}; +constexpr auto adjacent4_fn = [](auto... vals) { return (vals | ...); }; +constexpr auto adjacent4_result = array{7, 15, 15}; +constexpr auto adjacent5_fn = [](auto... vals) { return (vals - ...); }; +constexpr auto adjacent5_result = array{7, 7}; +constexpr auto adjacent6_fn = [](auto... vals) { return (... - vals); }; +constexpr auto adjacent6_result = array{-38}; +constexpr auto adjacent7_fn = [](auto...) { return 0; }; +constexpr array adjacent7_result{}; + +template +using test_range = + test::range}, + IsCommon, test::CanCompare{derived_from || IsCommon == test::Common::yes}, + test::ProxyRef{!derived_from}>; + +struct instantiator { + template + static constexpr void call() { + R r{some_ints}; + test_one<1>(r, adjacent1_fn, adjacent1_result); + test_one<2>(r, pairwise_fn, pairwise_result); +#ifndef _M_IX86 // fatal error C1060: compiler is out of heap space + test_one<4>(r, adjacent4_fn, adjacent4_result); +#endif // !defined(_M_IX86) + test_one<7>(r, adjacent7_fn, adjacent7_result); + test_adjacent0(r); + } +}; + +constexpr void instantiation_test() { +#ifdef TEST_EVERYTHING + test_fwd(); +#else // ^^^ test all forward range permutations / test only "interesting" permutations vvv + using test::Common, test::Sized; + + // The view is sensitive to category, commonality, and size, but oblivious to proxyness and differencing + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); +#endif // TEST_EVERYTHING +} + +template > +using move_only_view = test::range}, test::CanView::yes, + test::Copyability::move_only>; + +int main() { + { // Check views + // ... copyable + constexpr span s{some_ints}; + STATIC_ASSERT(test_one<4>(s, adjacent4_fn, adjacent4_result)); + test_one<4>(s, adjacent4_fn, adjacent4_result); + } + + { // ... move-only + test_one<1>(move_only_view{some_ints}, adjacent1_fn, adjacent1_result); + test_one<2>(move_only_view{some_ints}, pairwise_fn, pairwise_result); + test_one<3>( + move_only_view{some_ints}, adjacent3_fn, adjacent3_result); + test_one<4>( + move_only_view{some_ints}, adjacent4_fn, adjacent4_result); + test_one<5>( + move_only_view{some_ints}, adjacent5_fn, adjacent5_result); + test_one<6>( + move_only_view{some_ints}, adjacent6_fn, adjacent6_result); + } + + { // Check non-views + STATIC_ASSERT(test_one<2>(some_ints, pairwise_fn, pairwise_result)); + test_one<2>(some_ints, pairwise_fn, pairwise_result); + + auto vec = some_ints | ranges::to(); + test_one<3>(vec, adjacent3_fn, adjacent3_result); + + auto lst = some_ints | ranges::to(); + test_one<4>(lst, adjacent4_fn, adjacent4_result); + } + + { // Check empty range + constexpr auto to_float = [](int x, int y, int z) { return static_cast(x + y + z); }; + STATIC_ASSERT(test_one<3>(span{}, to_float, span{})); + test_one<3>(span{}, to_float, span{}); + } + + STATIC_ASSERT((instantiation_test(), true)); + instantiation_test(); +} diff --git a/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp b/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp index 1e545ab4f6..0aa3e9b765 100644 --- a/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp +++ b/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp @@ -1762,6 +1762,20 @@ STATIC_ASSERT(__cpp_lib_ranges_to_container == 202202L); #endif #endif +#if _HAS_CXX23 && defined(__cpp_lib_concepts) // TRANSITION, GH-395 +#ifndef __cpp_lib_ranges_zip +#error __cpp_lib_ranges_zip is not defined +#elif __cpp_lib_ranges_zip != 202110L +#error __cpp_lib_ranges_zip is not 202110L +#else +STATIC_ASSERT(__cpp_lib_ranges_zip == 202110L); +#endif +#else +#ifdef __cpp_lib_ranges_zip +#error __cpp_lib_ranges_zip is defined +#endif +#endif + #if _HAS_CXX17 #ifndef __cpp_lib_raw_memory_algorithms #error __cpp_lib_raw_memory_algorithms is not defined