From cedff2e72a4937840cd2c0b48ed5f8df559028a6 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Sun, 2 Aug 2020 14:36:06 +0200 Subject: [PATCH 1/5] Implement ranges::ref_view --- stl/inc/ranges | 63 +++++++++++++++ tests/std/test.lst | 1 + .../std/tests/P0896R4_ranges_ref_view/env.lst | 4 + .../tests/P0896R4_ranges_ref_view/test.cpp | 81 +++++++++++++++++++ 4 files changed, 149 insertions(+) create mode 100644 tests/std/tests/P0896R4_ranges_ref_view/env.lst create mode 100644 tests/std/tests/P0896R4_ranges_ref_view/test.cpp diff --git a/stl/inc/ranges b/stl/inc/ranges index 3024333376c..afbc948b839 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -29,6 +29,69 @@ namespace ranges { template concept viewable_range = range<_Rng> && (borrowed_range<_Rng> || view>); + + + // CLASS ranges::ref_view + // clang-format off + template + requires is_object_v<_Rng> + class ref_view : public view_interface> { + // clang-format on + void _Rvalue_poison(_Rng&); + void _Rvalue_poison(_Rng&&) = delete; + + public: + constexpr ref_view() noexcept = default; + + // clang-format off + template <_Not_same_as _OtherRng> + // requires convertible_to<_OtherRng, _Rng&> && requires { _Rvalue_poison(declval<_OtherRng>()); } + constexpr ref_view(_OtherRng&& _Range) + // clang-format on + : _Myrange(_STD addressof(static_cast<_Rng&>(_STD forward<_OtherRng>(_Range)))) {} + + constexpr _Rng& base() const { + return *_Myrange; + } + + constexpr iterator_t<_Rng> begin() const { + return _RANGES begin(*_Myrange); + } + + constexpr sentinel_t<_Rng> end() const { + return _RANGES end(*_Myrange); + } + + // clang-format off + constexpr bool empty() const + requires _Can_empty<_Rng> + { + // clang-format on + return _RANGES empty(*_Myrange); + } + + // clang-format off + constexpr auto size() const + requires sized_range<_Rng> + // clang-format on + { + return _RANGES size(*_Myrange); + } + + // clang-format off + constexpr auto data() const + requires contiguous_range<_Rng> + // clang-format on + { + return _RANGES data(*_Myrange); + } + + private: + _Rng* _Myrange = nullptr; + }; + + template + ref_view(_Rng&) -> ref_view<_Rng>; } // namespace ranges _STD_END diff --git a/tests/std/test.lst b/tests/std/test.lst index f6eda1e96ff..785a6008055 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -301,6 +301,7 @@ tests\P0896R4_ranges_alg_unique tests\P0896R4_ranges_alg_unique_copy tests\P0896R4_ranges_iterator_machinery tests\P0896R4_ranges_range_machinery +tests\P0896R4_ranges_ref_view tests\P0896R4_ranges_subrange tests\P0896R4_ranges_test_machinery tests\P0896R4_ranges_to_address diff --git a/tests/std/tests/P0896R4_ranges_ref_view/env.lst b/tests/std/tests/P0896R4_ranges_ref_view/env.lst new file mode 100644 index 00000000000..f3ccc8613c6 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_ref_view/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_matrix.lst diff --git a/tests/std/tests/P0896R4_ranges_ref_view/test.cpp b/tests/std/tests/P0896R4_ranges_ref_view/test.cpp new file mode 100644 index 00000000000..5bdfbba35d3 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_ref_view/test.cpp @@ -0,0 +1,81 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include + +#include +using namespace std; + +struct instantiator { + static constexpr int input[3] = {0, 1, 2}; + + template + static constexpr void call() { + using ranges::ref_view, ranges::iterator_t, ranges::sentinel_t, ranges::equal, ranges::begin, ranges::end; + + { // constructors + [[maybe_unused]] ref_view default_constructed{}; + + Read wrapped_input{input}; + [[maybe_unused]] ref_view same_range{wrapped_input}; + } + + { // access + Read wrapped_input{input}; + ref_view test_view{wrapped_input}; + auto& base_range = test_view.base(); + STATIC_ASSERT(same_as); + if constexpr (ranges::forward_range) { + assert(equal(base_range, wrapped_input)); + } + } + + { // iterators + Read wrapped_input{input}; + ref_view test_view{wrapped_input}; + const same_as> auto begin_iterator = test_view.begin(); + if constexpr (ranges::forward_range) { + assert(begin_iterator == begin(wrapped_input)); + } + + const same_as> auto end_iterator = test_view.end(); + if constexpr (ranges::forward_range) { + assert(end_iterator.peek() == end(wrapped_input).peek()); + } + } + + { // state + if constexpr (ranges::sized_range) { + Read wrapped_input{input}; + ref_view test_view{wrapped_input}; + + const same_as> auto ref_size = test_view.size(); + assert(ref_size == size(wrapped_input)); + } + + if constexpr (ranges::contiguous_range) { + Read wrapped_input{input}; + ref_view test_view{wrapped_input}; + + const same_as auto ref_data = test_view.data(); + assert(ref_data == input); + } + + span spanInput{input}; + ref_view span_view{spanInput}; + + const same_as auto ref_empty = span_view.empty(); + assert(!ref_empty); + } + } +}; + +int main() { + STATIC_ASSERT((test_in(), true)); + test_in(); +} From 35069af50f90926e36b97173c2d0e3d79fe1d72c Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Wed, 5 Aug 2020 17:26:01 +0200 Subject: [PATCH 2/5] Review comments from STL --- stl/inc/ranges | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index afbc948b839..6f0fdb68e91 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -30,13 +30,13 @@ namespace ranges { concept viewable_range = range<_Rng> && (borrowed_range<_Rng> || view>); - - // CLASS ranges::ref_view + // CLASS TEMPLATE ranges::ref_view // clang-format off template requires is_object_v<_Rng> class ref_view : public view_interface> { // clang-format on + private: void _Rvalue_poison(_Rng&); void _Rvalue_poison(_Rng&&) = delete; @@ -45,44 +45,43 @@ namespace ranges { // clang-format off template <_Not_same_as _OtherRng> - // requires convertible_to<_OtherRng, _Rng&> && requires { _Rvalue_poison(declval<_OtherRng>()); } - constexpr ref_view(_OtherRng&& _Range) + requires convertible_to<_OtherRng, _Rng&> && requires { _Rvalue_poison(_STD declval<_OtherRng>()); } + constexpr ref_view(_OtherRng&& _Other) // clang-format on - : _Myrange(_STD addressof(static_cast<_Rng&>(_STD forward<_OtherRng>(_Range)))) {} + : _Myrange(_STD addressof(static_cast<_Rng&>(_STD forward<_OtherRng>(_Other)))) {} - constexpr _Rng& base() const { + _NODISCARD constexpr _Rng& base() const noexcept(noexcept(*_Myrange)) /* strengthened */ { return *_Myrange; } - constexpr iterator_t<_Rng> begin() const { + _NODISCARD constexpr iterator_t<_Rng> begin() const + noexcept(noexcept(_RANGES begin(*_Myrange))) /* strengthened */ { return _RANGES begin(*_Myrange); } - constexpr sentinel_t<_Rng> end() const { + _NODISCARD constexpr sentinel_t<_Rng> end() const + noexcept(noexcept(_RANGES end(*_Myrange))) /* strengthened */ { return _RANGES end(*_Myrange); } // clang-format off - constexpr bool empty() const - requires _Can_empty<_Rng> - { + _NODISCARD constexpr bool empty() const noexcept(noexcept(_RANGES empty(*_Myrange))) /* strengthened */ + requires _Can_empty<_Rng> { // clang-format on return _RANGES empty(*_Myrange); } // clang-format off - constexpr auto size() const - requires sized_range<_Rng> - // clang-format on - { + _NODISCARD constexpr auto size() const noexcept(noexcept(_RANGES size(*_Myrange))) /* strengthened */ + requires sized_range<_Rng> { + // clang-format on return _RANGES size(*_Myrange); } // clang-format off - constexpr auto data() const - requires contiguous_range<_Rng> - // clang-format on - { + _NODISCARD constexpr auto data() const noexcept(noexcept(_RANGES data(*_Myrange))) /* strengthened */ + requires contiguous_range<_Rng> { + // clang-format on return _RANGES data(*_Myrange); } @@ -92,6 +91,9 @@ namespace ranges { template ref_view(_Rng&) -> ref_view<_Rng>; + + template + inline constexpr bool enable_borrowed_range> = true; } // namespace ranges _STD_END From aee50e98b04b4cfed086eaac2905f4798e507427 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Wed, 5 Aug 2020 18:21:58 +0200 Subject: [PATCH 3/5] Fix derp --- stl/inc/ranges | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 6f0fdb68e91..5fb5b06ea64 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -37,8 +37,8 @@ namespace ranges { class ref_view : public view_interface> { // clang-format on private: - void _Rvalue_poison(_Rng&); - void _Rvalue_poison(_Rng&&) = delete; + static constexpr void _Rvalue_poison(_Rng&); + static constexpr void _Rvalue_poison(_Rng&&) = delete; public: constexpr ref_view() noexcept = default; From b4958f5ba59a1079c7e86d58c364aefbf2489214 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Fri, 7 Aug 2020 12:49:25 +0200 Subject: [PATCH 4/5] Apply Caseys review comments --- stl/inc/ranges | 35 +++++------ tests/std/include/range_algorithm_support.hpp | 55 ++++++++++++++++ .../tests/P0896R4_ranges_ref_view/test.cpp | 62 +++++++++++++------ 3 files changed, 114 insertions(+), 38 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 5fb5b06ea64..1d26ea26863 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -37,8 +37,8 @@ namespace ranges { class ref_view : public view_interface> { // clang-format on private: - static constexpr void _Rvalue_poison(_Rng&); - static constexpr void _Rvalue_poison(_Rng&&) = delete; + static void _Rvalue_poison(_Rng&); + static void _Rvalue_poison(_Rng&&) = delete; public: constexpr ref_view() noexcept = default; @@ -46,47 +46,46 @@ namespace ranges { // clang-format off template <_Not_same_as _OtherRng> requires convertible_to<_OtherRng, _Rng&> && requires { _Rvalue_poison(_STD declval<_OtherRng>()); } - constexpr ref_view(_OtherRng&& _Other) + constexpr ref_view(_OtherRng&& _Other) noexcept(noexcept(static_cast<_Rng&>(_STD declval<_OtherRng>()))) // clang-format on - : _Myrange(_STD addressof(static_cast<_Rng&>(_STD forward<_OtherRng>(_Other)))) {} + : _Range{_STD addressof(static_cast<_Rng&>(_STD forward<_OtherRng>(_Other)))} {} - _NODISCARD constexpr _Rng& base() const noexcept(noexcept(*_Myrange)) /* strengthened */ { - return *_Myrange; + _NODISCARD constexpr _Rng& base() const noexcept /* strengthened */ { + return *_Range; } _NODISCARD constexpr iterator_t<_Rng> begin() const - noexcept(noexcept(_RANGES begin(*_Myrange))) /* strengthened */ { - return _RANGES begin(*_Myrange); + noexcept(noexcept(_RANGES begin(*_Range))) /* strengthened */ { + return _RANGES begin(*_Range); } - _NODISCARD constexpr sentinel_t<_Rng> end() const - noexcept(noexcept(_RANGES end(*_Myrange))) /* strengthened */ { - return _RANGES end(*_Myrange); + _NODISCARD constexpr sentinel_t<_Rng> end() const noexcept(noexcept(_RANGES end(*_Range))) /* strengthened */ { + return _RANGES end(*_Range); } // clang-format off - _NODISCARD constexpr bool empty() const noexcept(noexcept(_RANGES empty(*_Myrange))) /* strengthened */ + _NODISCARD constexpr bool empty() const noexcept(noexcept(_RANGES empty(*_Range))) /* strengthened */ requires _Can_empty<_Rng> { // clang-format on - return _RANGES empty(*_Myrange); + return _RANGES empty(*_Range); } // clang-format off - _NODISCARD constexpr auto size() const noexcept(noexcept(_RANGES size(*_Myrange))) /* strengthened */ + _NODISCARD constexpr auto size() const noexcept(noexcept(_RANGES size(*_Range))) /* strengthened */ requires sized_range<_Rng> { // clang-format on - return _RANGES size(*_Myrange); + return _RANGES size(*_Range); } // clang-format off - _NODISCARD constexpr auto data() const noexcept(noexcept(_RANGES data(*_Myrange))) /* strengthened */ + _NODISCARD constexpr auto data() const noexcept(noexcept(_RANGES data(*_Range))) /* strengthened */ requires contiguous_range<_Rng> { // clang-format on - return _RANGES data(*_Myrange); + return _RANGES data(*_Range); } private: - _Rng* _Myrange = nullptr; + _Rng* _Range = nullptr; }; template diff --git a/tests/std/include/range_algorithm_support.hpp b/tests/std/include/range_algorithm_support.hpp index de785c12555..285a8106d4e 100644 --- a/tests/std/include/range_algorithm_support.hpp +++ b/tests/std/include/range_algorithm_support.hpp @@ -958,6 +958,56 @@ struct with_output_ranges { } }; +template +struct with_input_or_output_ranges { + template + static constexpr void call() { + using namespace test; + using test::range; + + // For all ranges, IsCommon implies Eq. + // For single-pass ranges, Eq is uninteresting without IsCommon (there's only one valid iterator + // value at a time, and no reason to compare it with itself for equality). + Continuation::template call>(); + Continuation::template call>(); + Continuation::template call>(); + Continuation::template call>(); + + Continuation::template call>(); + Continuation::template call>(); + Continuation::template call>(); + Continuation::template call>(); + + Continuation::template call>(); + Continuation::template call>(); + Continuation::template call>(); + Continuation::template call>(); + + Continuation::template call>(); + Continuation::template call>(); + Continuation::template call>(); + Continuation::template call>(); + + with_output_ranges::template call(); + } +}; + template struct with_input_iterators { template @@ -1008,6 +1058,11 @@ constexpr void test_in() { with_input_ranges::call(); } +template +constexpr void test_inout() { + with_input_or_output_ranges::call(); +} + template constexpr void test_fwd() { with_forward_ranges::call(); diff --git a/tests/std/tests/P0896R4_ranges_ref_view/test.cpp b/tests/std/tests/P0896R4_ranges_ref_view/test.cpp index 5bdfbba35d3..e45c0250a2f 100644 --- a/tests/std/tests/P0896R4_ranges_ref_view/test.cpp +++ b/tests/std/tests/P0896R4_ranges_ref_view/test.cpp @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include -#include #include #include #include @@ -11,15 +10,28 @@ #include using namespace std; +// clang-format off +template +concept can_empty = requires (Range&& range) { ranges::empty(range); }; +// clang-format on + struct instantiator { - static constexpr int input[3] = {0, 1, 2}; - template + template static constexpr void call() { using ranges::ref_view, ranges::iterator_t, ranges::sentinel_t, ranges::equal, ranges::begin, ranges::end; + int input[3] = {0, 1, 2}; + + { // traits + STATIC_ASSERT(ranges::input_range || ranges::output_range); + STATIC_ASSERT(ranges::enable_borrowed_range>); + } { // constructors + STATIC_ASSERT(!constructible_from, Read>); + [[maybe_unused]] ref_view default_constructed{}; + STATIC_ASSERT(is_nothrow_default_constructible_v>); Read wrapped_input{input}; [[maybe_unused]] ref_view same_range{wrapped_input}; @@ -30,23 +42,19 @@ struct instantiator { ref_view test_view{wrapped_input}; auto& base_range = test_view.base(); STATIC_ASSERT(same_as); - if constexpr (ranges::forward_range) { - assert(equal(base_range, wrapped_input)); - } + assert(addressof(base_range) == addressof(wrapped_input)); + + STATIC_ASSERT(noexcept(test_view.base())); } { // iterators Read wrapped_input{input}; ref_view test_view{wrapped_input}; - const same_as> auto begin_iterator = test_view.begin(); - if constexpr (ranges::forward_range) { - assert(begin_iterator == begin(wrapped_input)); - } + const same_as> auto first = test_view.begin(); + assert(first.peek() == input); - const same_as> auto end_iterator = test_view.end(); - if constexpr (ranges::forward_range) { - assert(end_iterator.peek() == end(wrapped_input).peek()); - } + const same_as> auto last = test_view.end(); + assert(last.peek() == end(input)); } { // state @@ -56,26 +64,40 @@ struct instantiator { const same_as> auto ref_size = test_view.size(); assert(ref_size == size(wrapped_input)); + } else { + STATIC_ASSERT(!ranges::_Size::_Has_member, remove_cvref_t>>); } if constexpr (ranges::contiguous_range) { Read wrapped_input{input}; ref_view test_view{wrapped_input}; - const same_as auto ref_data = test_view.data(); + const same_as auto ref_data = test_view.data(); assert(ref_data == input); + } else { + STATIC_ASSERT(!ranges::_Data::_Has_member>); } + if constexpr (can_empty) { + Read wrapped_input{input}; + ref_view test_view{wrapped_input}; + + const same_as auto ref_empty = test_view.empty(); + assert(!ref_empty); + } else { + STATIC_ASSERT(!ranges::_Empty::_Has_member>); + } + } + + { // CTAD span spanInput{input}; ref_view span_view{spanInput}; - - const same_as auto ref_empty = span_view.empty(); - assert(!ref_empty); + STATIC_ASSERT(same_as>>); } } }; int main() { - STATIC_ASSERT((test_in(), true)); - test_in(); + STATIC_ASSERT((test_inout(), true)); + test_inout(); } From 296e15f4747eef6ed644dc61e82141a4aa5fb0e8 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Fri, 21 Aug 2020 11:45:38 -0700 Subject: [PATCH 5/5] Review comments --- stl/inc/ranges | 21 ++-- .../tests/P0896R4_ranges_ref_view/test.cpp | 118 ++++++++++++------ 2 files changed, 86 insertions(+), 53 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 1d26ea26863..fe0e967e46f 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -37,6 +37,8 @@ namespace ranges { class ref_view : public view_interface> { // clang-format on private: + _Rng* _Range = nullptr; + static void _Rvalue_poison(_Rng&); static void _Rvalue_poison(_Rng&&) = delete; @@ -45,10 +47,12 @@ namespace ranges { // clang-format off template <_Not_same_as _OtherRng> - requires convertible_to<_OtherRng, _Rng&> && requires { _Rvalue_poison(_STD declval<_OtherRng>()); } - constexpr ref_view(_OtherRng&& _Other) noexcept(noexcept(static_cast<_Rng&>(_STD declval<_OtherRng>()))) - // clang-format on - : _Range{_STD addressof(static_cast<_Rng&>(_STD forward<_OtherRng>(_Other)))} {} + constexpr ref_view(_OtherRng&& _Other) noexcept( + noexcept(static_cast<_Rng&>(_STD forward<_OtherRng>(_Other)))) // strengthened + requires convertible_to<_OtherRng, _Rng&> && requires { + _Rvalue_poison(static_cast<_OtherRng&&>(_Other)); + } : _Range{_STD addressof(static_cast<_Rng&>(_STD forward<_OtherRng>(_Other)))} {} + // clang-format on _NODISCARD constexpr _Rng& base() const noexcept /* strengthened */ { return *_Range; @@ -63,29 +67,20 @@ namespace ranges { return _RANGES end(*_Range); } - // clang-format off _NODISCARD constexpr bool empty() const noexcept(noexcept(_RANGES empty(*_Range))) /* strengthened */ requires _Can_empty<_Rng> { - // clang-format on return _RANGES empty(*_Range); } - // clang-format off _NODISCARD constexpr auto size() const noexcept(noexcept(_RANGES size(*_Range))) /* strengthened */ requires sized_range<_Rng> { - // clang-format on return _RANGES size(*_Range); } - // clang-format off _NODISCARD constexpr auto data() const noexcept(noexcept(_RANGES data(*_Range))) /* strengthened */ requires contiguous_range<_Rng> { - // clang-format on return _RANGES data(*_Range); } - - private: - _Rng* _Range = nullptr; }; template diff --git a/tests/std/tests/P0896R4_ranges_ref_view/test.cpp b/tests/std/tests/P0896R4_ranges_ref_view/test.cpp index e45c0250a2f..0a69e1e5561 100644 --- a/tests/std/tests/P0896R4_ranges_ref_view/test.cpp +++ b/tests/std/tests/P0896R4_ranges_ref_view/test.cpp @@ -4,88 +4,126 @@ #include #include #include +#include #include #include +#include #include using namespace std; // clang-format off -template -concept can_empty = requires (Range&& range) { ranges::empty(range); }; +template +concept can_empty = requires(Range& r) { ranges::empty(r); }; +template +concept can_data = requires(Range& r) { ranges::data(r); }; +template +concept can_size = requires(Range& r) { ranges::size(r); }; // clang-format on struct instantiator { - - template + template static constexpr void call() { - using ranges::ref_view, ranges::iterator_t, ranges::sentinel_t, ranges::equal, ranges::begin, ranges::end; + using ranges::ref_view, ranges::begin, ranges::end, ranges::forward_range; int input[3] = {0, 1, 2}; { // traits - STATIC_ASSERT(ranges::input_range || ranges::output_range); - STATIC_ASSERT(ranges::enable_borrowed_range>); + STATIC_ASSERT(ranges::input_range || ranges::output_range); + STATIC_ASSERT(ranges::enable_borrowed_range>); } - { // constructors - STATIC_ASSERT(!constructible_from, Read>); + { // constructors and assignment operators + STATIC_ASSERT(!constructible_from, R>); + + ref_view default_constructed{}; + STATIC_ASSERT(is_nothrow_default_constructible_v>); - [[maybe_unused]] ref_view default_constructed{}; - STATIC_ASSERT(is_nothrow_default_constructible_v>); + R wrapped_input{input}; + ref_view same_range{wrapped_input}; + STATIC_ASSERT(is_nothrow_constructible_v, R&>); + + auto copy_constructed = same_range; + if constexpr (forward_range) { + assert(copy_constructed.begin().peek() == begin(input)); + } + assert(copy_constructed.end().peek() == end(input)); - Read wrapped_input{input}; - [[maybe_unused]] ref_view same_range{wrapped_input}; + default_constructed = copy_constructed; + if constexpr (forward_range) { + assert(default_constructed.begin().peek() == begin(input)); + } + assert(default_constructed.end().peek() == end(input)); + + [[maybe_unused]] auto move_constructed = std::move(default_constructed); + if constexpr (forward_range) { + assert(move_constructed.begin().peek() == begin(input)); + } + assert(move_constructed.end().peek() == end(input)); + + same_range = std::move(copy_constructed); + if constexpr (forward_range) { + assert(same_range.begin().peek() == begin(input)); + } + assert(same_range.end().peek() == end(input)); } { // access - Read wrapped_input{input}; - ref_view test_view{wrapped_input}; - auto& base_range = test_view.base(); - STATIC_ASSERT(same_as); + R wrapped_input{input}; + ref_view test_view{wrapped_input}; + same_as auto& base_range = as_const(test_view).base(); assert(addressof(base_range) == addressof(wrapped_input)); - STATIC_ASSERT(noexcept(test_view.base())); + STATIC_ASSERT(noexcept(as_const(test_view).base())); } { // iterators - Read wrapped_input{input}; - ref_view test_view{wrapped_input}; - const same_as> auto first = test_view.begin(); + R wrapped_input{input}; + ref_view test_view{wrapped_input}; + const same_as> auto first = as_const(test_view).begin(); assert(first.peek() == input); + STATIC_ASSERT(noexcept(as_const(test_view).begin()) == noexcept(wrapped_input.begin())); - const same_as> auto last = test_view.end(); + const same_as> auto last = as_const(test_view).end(); assert(last.peek() == end(input)); + STATIC_ASSERT(noexcept(as_const(test_view).end()) == noexcept(wrapped_input.end())); } { // state - if constexpr (ranges::sized_range) { - Read wrapped_input{input}; - ref_view test_view{wrapped_input}; + STATIC_ASSERT(can_size> == ranges::sized_range); + if constexpr (ranges::sized_range) { + R wrapped_input{input}; + ref_view test_view{wrapped_input}; - const same_as> auto ref_size = test_view.size(); + const same_as> auto ref_size = as_const(test_view).size(); assert(ref_size == size(wrapped_input)); - } else { - STATIC_ASSERT(!ranges::_Size::_Has_member, remove_cvref_t>>); + + STATIC_ASSERT(noexcept(as_const(test_view).size()) == noexcept(wrapped_input.size())); } - if constexpr (ranges::contiguous_range) { - Read wrapped_input{input}; - ref_view test_view{wrapped_input}; + STATIC_ASSERT(can_data> == ranges::contiguous_range); + if constexpr (ranges::contiguous_range) { + R wrapped_input{input}; + ref_view test_view{wrapped_input}; - const same_as auto ref_data = test_view.data(); + const same_as auto ref_data = as_const(test_view).data(); assert(ref_data == input); - } else { - STATIC_ASSERT(!ranges::_Data::_Has_member>); + + STATIC_ASSERT(noexcept(as_const(test_view).data()) == noexcept(wrapped_input.data())); } - if constexpr (can_empty) { - Read wrapped_input{input}; - ref_view test_view{wrapped_input}; + STATIC_ASSERT(can_empty> == can_empty); + if constexpr (can_empty) { + R wrapped_input{input}; + ref_view test_view{wrapped_input}; - const same_as auto ref_empty = test_view.empty(); + const same_as auto ref_empty = as_const(test_view).empty(); assert(!ref_empty); - } else { - STATIC_ASSERT(!ranges::_Empty::_Has_member>); + + STATIC_ASSERT(noexcept(as_const(test_view).empty()) == noexcept(ranges::empty(wrapped_input))); + + R empty_range{}; + ref_view empty_view{empty_range}; + assert(empty_view.empty()); } }