diff --git a/stl/inc/ranges b/stl/inc/ranges index 3024333376c..fe0e967e46f 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -29,6 +29,65 @@ namespace ranges { template concept viewable_range = range<_Rng> && (borrowed_range<_Rng> || 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: + _Rng* _Range = nullptr; + + static void _Rvalue_poison(_Rng&); + static void _Rvalue_poison(_Rng&&) = delete; + + public: + constexpr ref_view() noexcept = default; + + // clang-format off + template <_Not_same_as _OtherRng> + 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; + } + + _NODISCARD constexpr iterator_t<_Rng> begin() const + noexcept(noexcept(_RANGES begin(*_Range))) /* strengthened */ { + return _RANGES begin(*_Range); + } + + _NODISCARD constexpr sentinel_t<_Rng> end() const noexcept(noexcept(_RANGES end(*_Range))) /* strengthened */ { + return _RANGES end(*_Range); + } + + _NODISCARD constexpr bool empty() const noexcept(noexcept(_RANGES empty(*_Range))) /* strengthened */ + requires _Can_empty<_Rng> { + return _RANGES empty(*_Range); + } + + _NODISCARD constexpr auto size() const noexcept(noexcept(_RANGES size(*_Range))) /* strengthened */ + requires sized_range<_Rng> { + return _RANGES size(*_Range); + } + + _NODISCARD constexpr auto data() const noexcept(noexcept(_RANGES data(*_Range))) /* strengthened */ + requires contiguous_range<_Rng> { + return _RANGES data(*_Range); + } + }; + + template + ref_view(_Rng&) -> ref_view<_Rng>; + + template + inline constexpr bool enable_borrowed_range> = true; } // namespace ranges _STD_END 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/test.lst b/tests/std/test.lst index 70f23e250c2..991ff68ea86 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -304,6 +304,7 @@ tests\P0896R4_ranges_alg_unique_copy tests\P0896R4_ranges_algorithm_machinery 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..0a69e1e5561 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_ref_view/test.cpp @@ -0,0 +1,141 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include + +#include +using namespace std; + +// clang-format off +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 + static constexpr void call() { + 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>); + } + + { // constructors and assignment operators + STATIC_ASSERT(!constructible_from, R>); + + 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)); + + 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 + 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(as_const(test_view).base())); + } + + { // iterators + 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 = as_const(test_view).end(); + assert(last.peek() == end(input)); + STATIC_ASSERT(noexcept(as_const(test_view).end()) == noexcept(wrapped_input.end())); + } + + { // state + 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 = as_const(test_view).size(); + assert(ref_size == size(wrapped_input)); + + STATIC_ASSERT(noexcept(as_const(test_view).size()) == noexcept(wrapped_input.size())); + } + + 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 = as_const(test_view).data(); + assert(ref_data == input); + + STATIC_ASSERT(noexcept(as_const(test_view).data()) == noexcept(wrapped_input.data())); + } + + 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 = as_const(test_view).empty(); + assert(!ref_empty); + + 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()); + } + } + + { // CTAD + span spanInput{input}; + ref_view span_view{spanInput}; + STATIC_ASSERT(same_as>>); + } + } +}; + +int main() { + STATIC_ASSERT((test_inout(), true)); + test_inout(); +}