diff --git a/stl/inc/ranges b/stl/inc/ranges index e597cd08fbb..bcc4459f5b2 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -5046,6 +5046,138 @@ namespace ranges { _EXPORT_STD inline constexpr _Reverse_fn reverse; } // namespace views +#if _HAS_CXX23 + _EXPORT_STD template + requires input_range<_Vw> + class as_const_view : public view_interface> { + private: + /* [[no_unique_address]] */ _Vw _Range{}; + + public: + // clang-format off + as_const_view() requires default_initializable<_Vw> = default; + // clang-format on + + constexpr explicit as_const_view(_Vw _Range_) noexcept(is_nothrow_move_constructible_v<_Vw>) // strengthened + : _Range(_STD move(_Range_)) {} + + _NODISCARD constexpr _Vw base() const& noexcept(is_nothrow_copy_constructible_v<_Vw>) // strengthened + requires copy_constructible<_Vw> + { + return _Range; + } + + _NODISCARD constexpr _Vw base() && noexcept(is_nothrow_move_constructible_v<_Vw>) /* strengthened */ { + return _STD move(_Range); + } + + _NODISCARD constexpr auto begin() noexcept(noexcept(_RANGES cbegin(_Range))) // strengthened + requires (!_Simple_view<_Vw>) + { + return _RANGES cbegin(_Range); + } + + _NODISCARD constexpr auto begin() const noexcept(noexcept(_RANGES cbegin(_Range))) // strengthened + requires range + { + return _RANGES cbegin(_Range); + } + + _NODISCARD constexpr auto end() noexcept(noexcept(_RANGES cend(_Range))) // strengthened + requires (!_Simple_view<_Vw>) + { + return _RANGES cend(_Range); + } + + _NODISCARD constexpr auto end() const noexcept(noexcept(_RANGES cend(_Range))) // strengthened + requires range + { + return _RANGES cend(_Range); + } + + _NODISCARD constexpr auto size() noexcept(noexcept(_RANGES size(_Range))) // strengthened + requires sized_range<_Vw> + { + return _RANGES size(_Range); + } + + _NODISCARD constexpr auto size() const noexcept(noexcept(_RANGES size(_Range))) // strengthened + requires sized_range + { + return _RANGES size(_Range); + } + }; + + template + as_const_view(_Rng&&) -> as_const_view>; + + template + inline constexpr bool enable_borrowed_range> = enable_borrowed_range<_Rng>; + + namespace views { + template + concept _Can_as_const = requires(_Rng&& __r) { as_const_view{static_cast<_Rng&&>(__r)}; }; + + class _As_const_fn : public _Pipe::_Base<_As_const_fn> { + private: + enum class _St { _None, _All, _Reconstruct_span, _Reconstruct_ref, _Ref, _As_const }; + + template + static constexpr bool _Can_reconstruct_ref_view_v = false; + + template + requires constant_range + static constexpr bool _Can_reconstruct_ref_view_v> = true; + + template + _NODISCARD static _CONSTEVAL _Choice_t<_St> _Choose() noexcept { + using _Ty = remove_cvref_t<_Rng>; + + if constexpr (constant_range>) { + return {_St::_All, noexcept(views::all(_STD declval<_Rng>()))}; + } else if constexpr (_Is_span_v<_Ty>) { + return {_St::_Reconstruct_span, true}; + } else if constexpr (_Can_reconstruct_ref_view_v<_Ty>) { + return {_St::_Reconstruct_ref, noexcept(ref_view{_STD as_const(_STD declval<_Rng>().base())})}; + } else if constexpr (is_lvalue_reference_v<_Rng> && constant_range && !view<_Ty>) { + return {_St::_Ref, noexcept(ref_view{_STD as_const(_STD declval<_Rng>())})}; + } else if constexpr (_Can_as_const<_Rng>) { + return {_St::_As_const, noexcept(as_const_view{_STD declval<_Rng>()})}; + } else { + return {_St::_None}; + } + } + + template + static constexpr _Choice_t<_St> _Choice = _Choose<_Rng>(); + + public: + template + requires (_Choice<_Rng>._Strategy != _St::_None) + _NODISCARD constexpr auto operator()(_Rng&& _Range) const noexcept(_Choice<_Rng>._No_throw) { + using _Ty = remove_cvref_t<_Rng>; + constexpr _St _Strat = _Choice<_Rng>._Strategy; + + if constexpr (_Strat == _St::_All) { + return views::all(_STD forward<_Rng>(_Range)); + } else if constexpr (_Strat == _St::_Reconstruct_span) { + return span{_STD forward<_Rng>(_Range)}; + } else if constexpr (_Strat == _St::_Reconstruct_ref) { + return ref_view{_STD as_const(_STD forward<_Rng>(_Range).base())}; + } else if constexpr (_Strat == _St::_Ref) { + return ref_view{_STD as_const(_STD forward<_Rng>(_Range))}; + } else if constexpr (_Strat == _St::_As_const) { + return as_const_view{_STD forward<_Rng>(_Range)}; + } else { + static_assert(_Always_false<_Rng>, "Should be unreachable"); + } + } + }; + + _EXPORT_STD inline constexpr _As_const_fn as_const; + } // namespace views +#endif // _HAS_CXX23 + template concept _Has_tuple_element = // requires(_Tuple __t) { diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index ad10a9d0e8b..461432b6969 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -325,7 +325,6 @@ // P2186R2 Removing Garbage Collection Support // P2273R3 constexpr unique_ptr // P2278R4 cbegin Should Always Return A Constant Iterator -// (missing views::as_const) // P2291R3 constexpr Integral // P2302R4 ranges::contains, ranges::contains_subrange // P2321R2 zip @@ -1673,6 +1672,7 @@ _EMIT_STL_ERROR(STL1004, "C++98 unexpected() is incompatible with C++23 unexpect #ifdef __cpp_lib_concepts #define __cpp_lib_out_ptr 202106L +#define __cpp_lib_ranges_as_const 202207L #define __cpp_lib_ranges_as_rvalue 202207L #define __cpp_lib_ranges_chunk 202202L #define __cpp_lib_ranges_chunk_by 202202L diff --git a/tests/std/test.lst b/tests/std/test.lst index 9af561348bf..00970154e91 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -551,6 +551,7 @@ tests\P2278R4_basic_const_iterator tests\P2278R4_const_span tests\P2278R4_ranges_const_iterator_machinery tests\P2278R4_ranges_const_range_machinery +tests\P2278R4_views_as_const tests\P2302R4_ranges_alg_contains tests\P2302R4_ranges_alg_contains_subrange tests\P2321R2_proxy_reference diff --git a/tests/std/tests/P2278R4_views_as_const/env.lst b/tests/std/tests/P2278R4_views_as_const/env.lst new file mode 100644 index 00000000000..18e2d7c71ec --- /dev/null +++ b/tests/std/tests/P2278R4_views_as_const/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_latest_matrix.lst diff --git a/tests/std/tests/P2278R4_views_as_const/test.cpp b/tests/std/tests/P2278R4_views_as_const/test.cpp new file mode 100644 index 00000000000..679cab731ba --- /dev/null +++ b/tests/std/tests/P2278R4_views_as_const/test.cpp @@ -0,0 +1,558 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace std; + +template +concept CanViewAsConst = requires(Rng&& r) { views::as_const(forward(r)); }; + +template +struct RefViewUnderlyingType {}; + +template +struct RefViewUnderlyingType> { + using type = T; +}; + +template +constexpr bool CanReconstructRefView = false; + +template +constexpr bool CanReconstructRefView> = ranges::constant_range; + +template +constexpr bool test_one(Rng&& rng, Expected&& expected) { + using ranges::ref_view, ranges::as_const_view, ranges::begin, ranges::end, ranges::iterator_t, ranges::sentinel_t, + ranges::prev, ranges::forward_range, ranges::bidirectional_range, ranges::random_access_range, + ranges::contiguous_range, ranges::common_range, ranges::sized_range, ranges::constant_range; + using V = views::all_t; + using R = as_const_view; + + constexpr bool is_view = ranges::view>; + + STATIC_ASSERT(ranges::view); + STATIC_ASSERT(ranges::input_range == ranges::input_range); + STATIC_ASSERT(forward_range == forward_range); + STATIC_ASSERT(bidirectional_range == bidirectional_range); + STATIC_ASSERT(random_access_range == random_access_range); + STATIC_ASSERT(contiguous_range == contiguous_range); + STATIC_ASSERT(constant_range); + + STATIC_ASSERT(!indirectly_writable, ranges::range_value_t>); + STATIC_ASSERT(!indirectly_writable, ranges::range_reference_t>); + + // Validate default-initializability + STATIC_ASSERT(default_initializable == default_initializable); + + // Validate borrowed_range + STATIC_ASSERT(ranges::borrowed_range == ranges::borrowed_range); + + // Validate range adaptor object + if constexpr (constant_range) { // range adaptor results in views::all_t + // ... with lvalue argument + STATIC_ASSERT(CanViewAsConst == (!is_view || copy_constructible) ); + if constexpr (CanViewAsConst) { + constexpr bool is_noexcept = !is_view || is_nothrow_copy_constructible_v; + + STATIC_ASSERT(same_as(rng))), V>); + STATIC_ASSERT(noexcept(views::as_const(std::forward(rng))) == is_noexcept); + + STATIC_ASSERT(same_as(rng) | views::as_const), V>); + STATIC_ASSERT(noexcept(std::forward(rng) | views::as_const) == is_noexcept); + } + + // ... with const lvalue argument + STATIC_ASSERT(CanViewAsConst&> == (!is_view || copy_constructible) ); + if constexpr (CanViewAsConst&>) { + using VC = views::all_t&>; + constexpr bool is_noexcept = !is_view || is_nothrow_copy_constructible_v; + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::as_const(as_const(rng))) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(as_const(rng) | views::as_const) == is_noexcept); + } + + // ... with rvalue argument + STATIC_ASSERT(CanViewAsConst> == (is_view || movable>) ); + if constexpr (CanViewAsConst>) { + using VS = views::all_t>; + constexpr bool is_noexcept = is_nothrow_move_constructible_v; + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::as_const(std::move(rng))) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(std::move(rng) | views::as_const) == is_noexcept); + } + + // ... with const rvalue argument + STATIC_ASSERT(CanViewAsConst> == (is_view && copy_constructible) ); + if constexpr (CanViewAsConst>) { + constexpr bool is_noexcept = is_nothrow_copy_constructible_v; + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::as_const(std::move(as_const(rng)))) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(std::move(as_const(rng)) | views::as_const) == is_noexcept); + } + } else if constexpr (_Is_span_v) { // range adaptor results in span reconstructed from span + using ConstSpan = span; + + { // ... with lvalue argument + STATIC_ASSERT(same_as(rng))), ConstSpan>); + STATIC_ASSERT(noexcept(views::as_const(std::forward(rng)))); + + STATIC_ASSERT(same_as(rng) | views::as_const), ConstSpan>); + STATIC_ASSERT(noexcept(std::forward(rng) | views::as_const)); + } + + { // ... with const lvalue argument + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::as_const(std::as_const(rng)))); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(std::as_const(rng) | views::as_const)); + } + + { // ... with rvalue argument + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::as_const(std::move(rng)))); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(std::move(rng) | views::as_const)); + } + + { // ... with const rvalue argument + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::as_const(std::move(std::as_const(rng))))); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(std::move(std::as_const(rng)) | views::as_const)); + } + } else if constexpr (CanReconstructRefView) { + // range adaptor results in ref_view reconstructed from ref_view + using ReconstructedRefView = ref_view::type>; + + { // ... with lvalue argument + STATIC_ASSERT(same_as(rng))), ReconstructedRefView>); + STATIC_ASSERT(noexcept(views::as_const(std::forward(rng)))); + + STATIC_ASSERT(same_as(rng) | views::as_const), ReconstructedRefView>); + STATIC_ASSERT(noexcept(std::forward(rng) | views::as_const)); + } + + { // ... with const lvalue argument + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::as_const(std::as_const(rng)))); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(std::as_const(rng) | views::as_const)); + } + + { //... with rvalue argument + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::as_const(std::move(rng)))); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(std::move(rng) | views::as_const)); + } + + { // ... with const rvalue argument + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::as_const(std::move(std::as_const(rng))))); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(std::move(std::as_const(rng)) | views::as_const)); + } + + } else if constexpr (is_lvalue_reference_v && constant_range> + && !is_view) { // range adaptor results in ref_view + using ConstRefView = ranges::ref_view>; + + // ... with lvalue argument + STATIC_ASSERT(CanViewAsConst == copy_constructible); + if constexpr (CanViewAsConst) { + STATIC_ASSERT(same_as(rng))), ConstRefView>); + STATIC_ASSERT(noexcept(views::as_const(std::forward(rng)))); + + STATIC_ASSERT(same_as(rng) | views::as_const), ConstRefView>); + STATIC_ASSERT(noexcept(std::forward(rng) | views::as_const)); + } + + // ... with const lvalue argument + STATIC_ASSERT(CanViewAsConst&> == copy_constructible); + if constexpr (CanViewAsConst&>) { + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::as_const(as_const(rng)))); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(as_const(rng) | views::as_const)); + } + } else { // range adaptor results in as_const_view + // ... with lvalue argument + STATIC_ASSERT(CanViewAsConst == (!is_view || copy_constructible) ); + if constexpr (CanViewAsConst) { + constexpr bool is_noexcept = !is_view || is_nothrow_copy_constructible_v; + + STATIC_ASSERT(same_as(rng))), R>); + STATIC_ASSERT(noexcept(views::as_const(std::forward(rng))) == is_noexcept); + + STATIC_ASSERT(same_as(rng) | views::as_const), R>); + STATIC_ASSERT(noexcept(std::forward(rng) | views::as_const) == is_noexcept); + } + + // ... with const lvalue argument + STATIC_ASSERT(CanViewAsConst&> == (!is_view || copy_constructible) ); + if constexpr (CanViewAsConst&> + && !constant_range&>) { + using RC = as_const_view&>>; + constexpr bool is_noexcept = !is_view || is_nothrow_copy_constructible_v; + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::as_const(std::as_const(rng))) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(std::as_const(rng) | views::as_const) == is_noexcept); + } + + // ... with rvalue argument + STATIC_ASSERT(CanViewAsConst> == (is_view || movable>) ); + if constexpr (CanViewAsConst>) { + using RS = as_const_view>>; + constexpr bool is_noexcept = is_nothrow_move_constructible_v; + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::as_const(std::move(rng))) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(std::move(rng) | views::as_const) == is_noexcept); + } + + // ... with const rvalue argument + STATIC_ASSERT(CanViewAsConst> == (is_view && copy_constructible) ); + if constexpr (CanViewAsConst>) { + constexpr bool is_noexcept = is_nothrow_copy_constructible_v; + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::as_const(std::move(std::as_const(rng)))) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(std::move(std::as_const(rng)) | views::as_const) == is_noexcept); + } + } + + // Validate deduction guide + same_as auto r = as_const_view{std::forward(rng)}; + + // Validate as_const_view::size + STATIC_ASSERT(CanMemberSize == sized_range); + if constexpr (CanMemberSize) { + same_as> auto s = r.size(); + assert(_To_unsigned_like(s) == ranges::size(expected)); + STATIC_ASSERT(noexcept(r.size()) == noexcept(ranges::size(as_const(rng)))); + } + + // Validate as_const_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)); + STATIC_ASSERT(noexcept(as_const(r).size()) == noexcept(ranges::size(rng))); + } + + const bool is_empty = ranges::empty(expected); + + // Validate view_interface::empty and operator bool + STATIC_ASSERT(CanMemberEmpty == (forward_range || sized_range) ); + STATIC_ASSERT(CanBool == CanEmpty); + if constexpr (CanMemberEmpty) { + assert(r.empty() == is_empty); + assert(static_cast(r) == !is_empty); + } + + // Validate view_interface::empty and operator bool (const) + STATIC_ASSERT(CanMemberEmpty == (forward_range || sized_range) ); + STATIC_ASSERT(CanBool == CanEmpty); + if constexpr (CanMemberEmpty) { + assert(as_const(r).empty() == is_empty); + assert(static_cast(as_const(r)) == !is_empty); + } + + assert(ranges::equal(r, expected)); + if (!forward_range) { // intentionally not if constexpr + return true; + } + + // Validate as_const_view::begin + STATIC_ASSERT(CanMemberBegin); + { + const same_as> auto i = r.begin(); + if (!is_empty) { + assert(*i == *begin(expected)); + } + + if constexpr (copy_constructible) { + auto r2 = r; + const same_as> auto i2 = r2.begin(); + if (!is_empty) { + assert(*i2 == *i); + } + } + } + + // Validate as_const_view::begin (const) + STATIC_ASSERT(CanMemberBegin == ranges::range); + if constexpr (CanMemberBegin) { + const same_as> auto ci = as_const(r).begin(); + if (!is_empty) { + assert(*ci == *begin(expected)); + } + + if constexpr (copy_constructible) { + const auto cr2 = r; + const same_as> auto ci2 = cr2.begin(); + if (!is_empty) { + assert(*ci2 == *ci); + } + } + } + + // Validate as_const_view::end + STATIC_ASSERT(CanMemberEnd); + { + const same_as> auto s = r.end(); + assert((r.begin() == s) == is_empty); + STATIC_ASSERT(common_range == common_range); + if constexpr (common_range && bidirectional_range) { + if (!is_empty) { + assert(*prev(s) == *prev(end(expected))); + } + + if constexpr (copy_constructible) { + auto r2 = r; + if (!is_empty) { + assert(*prev(r2.end()) == *prev(end(expected))); + } + } + } + } + + // Validate as_const_view::end (const) + STATIC_ASSERT(CanMemberEnd == ranges::range); + if constexpr (CanMemberEnd) { + const same_as> auto cs = as_const(r).end(); + assert((as_const(r).begin() == cs) == is_empty); + STATIC_ASSERT(common_range == common_range); + if constexpr (common_range && bidirectional_range) { + if (!is_empty) { + assert(*prev(cs) == *prev(end(expected))); + } + + if constexpr (copy_constructible) { + const auto r2 = r; + if (!is_empty) { + assert(*prev(r2.end()) == *prev(end(expected))); + } + } + } + } + + // Validate view_interface::data + STATIC_ASSERT(CanMemberData == contiguous_range); + STATIC_ASSERT(CanData == contiguous_range); + if constexpr (contiguous_range) { + const same_as>*> auto ptr = r.data(); + assert(ptr == to_address(r.begin())); + } + + // Validate view_interface::data (const) + STATIC_ASSERT(CanMemberData == contiguous_range); + STATIC_ASSERT(CanData == contiguous_range); + if constexpr (contiguous_range) { + const same_as>*> auto ptr = as_const(r).data(); + assert(ptr == to_address(as_const(r).begin())); + } + + if (!is_empty) { + // Validate view_interface::operator[] + STATIC_ASSERT(CanIndex == random_access_range); + if constexpr (CanIndex) { + assert(r[0] == expected[0]); + } + + // Validate view_interface::operator[] (const) + STATIC_ASSERT(CanIndex == random_access_range); + if constexpr (CanIndex) { + assert(as_const(r)[0] == expected[0]); + } + + // Validate view_interface::front + STATIC_ASSERT(CanMemberFront == forward_range); + if constexpr (CanMemberFront) { + assert(r.front() == *begin(expected)); + } + + // Validate view_interface::front (const) + STATIC_ASSERT(CanMemberFront == forward_range); + if constexpr (CanMemberFront) { + assert(as_const(r).front() == *begin(expected)); + } + + // Validate view_interface::back + STATIC_ASSERT(CanMemberBack == (bidirectional_range && common_range) ); + if constexpr (CanMemberBack) { + assert(r.back() == *prev(end(expected))); + } + + // Validate view_interface::back (const) + STATIC_ASSERT(CanMemberBack == (bidirectional_range && common_range) ); + if constexpr (CanMemberBack) { + assert(as_const(r).back() == *prev(end(expected))); + } + } + + // Validate as_const_view::base() const& + STATIC_ASSERT(CanMemberBase == copy_constructible); + if constexpr (copy_constructible) { + same_as auto b1 = as_const(r).base(); + STATIC_ASSERT(noexcept(as_const(r).base()) == is_nothrow_copy_constructible_v); + if (!is_empty) { + assert(*b1.begin() == *begin(expected)); + } + } + + // Validate as_const_view::base() && + same_as auto b2 = std::move(r).base(); + STATIC_ASSERT(noexcept(std::move(r).base()) == is_nothrow_move_constructible_v); + if (!is_empty) { + assert(*b2.begin() == *begin(expected)); + } + + return true; +} + +constexpr int some_ints[] = {0, 3, 6, 9, 12, 15}; + +struct instantiator { + template + static constexpr void call() { + R r{some_ints}; + test_one(r, span{some_ints}); + } +}; + +template +using test_range = + test::range}, + IsCommon, test::CanCompare{derived_from || IsCommon == test::Common::yes}, + test::ProxyRef{!derived_from}>; + +constexpr bool instantiation_test() { +#ifdef TEST_EVERYTHING + test_in(); +#else // ^^^ test all input permutations / test only "interesting" permutations vvv + using test::Common, test::Sized; + + // The view is sensitive to category, commonality, and size, but oblivious to differencing and proxyness + 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>(); + + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); +#endif // TEST_EVERYTHING + return true; +} + +template > +using move_only_view = test::range}, + test::CanView::yes, test::Copyability::move_only>; + +int main() { +#ifndef __clang__ // TRANSITION, LLVM-44833 + { // Validate views + // ... copyable + constexpr span s{some_ints}; + STATIC_ASSERT(test_one(s, some_ints)); + test_one(s, some_ints); + } +#endif // TRANSITION, LLVM-44833 + + { // ... move-only + test_one(move_only_view{some_ints}, some_ints); + test_one(move_only_view{some_ints}, some_ints); + test_one(move_only_view{some_ints}, some_ints); + test_one(move_only_view{some_ints}, some_ints); + test_one(move_only_view{some_ints}, some_ints); + test_one(move_only_view{some_ints}, some_ints); + test_one(move_only_view{some_ints}, some_ints); + test_one(move_only_view{some_ints}, some_ints); + } + + { // Validate non-views + STATIC_ASSERT(test_one(some_ints, some_ints)); + test_one(some_ints, some_ints); + + // Test with lvalue, rvalue, and wrapped in ref_view non-views + auto vec = some_ints | ranges::to(); + test_one(vec, some_ints); + test_one(ranges::ref_view{vec}, some_ints); + test_one(some_ints | ranges::to(), some_ints); + + auto lst = some_ints | ranges::to(); + test_one(lst, some_ints); + test_one(ranges::ref_view{lst}, some_ints); + test_one(some_ints | ranges::to(), some_ints); + } + + { // Validate single_view + static constexpr int one_int[1] = {333}; + STATIC_ASSERT(test_one(views::single(333), one_int)); + test_one(views::single(333), one_int); + } + +#ifndef __clang__ // TRANSITION, LLVM-44833 + { // empty range + using Span = span; + STATIC_ASSERT(test_one(Span{}, Span{})); + test_one(Span{}, Span{}); + } +#endif // TRANSITION, LLVM-44833 + + STATIC_ASSERT(instantiation_test()); + 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 80265e0975e..ed8d3c50e70 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 @@ -1518,6 +1518,20 @@ STATIC_ASSERT(__cpp_lib_ranges == 202110L); #endif #endif +#if _HAS_CXX23 && defined(__cpp_lib_concepts) // TRANSITION, GH-395 +#ifndef __cpp_lib_ranges_as_const +#error __cpp_lib_ranges_as_const is not defined +#elif __cpp_lib_ranges_as_const != 202207L +#error __cpp_lib_ranges_as_const is not 202207L +#else +STATIC_ASSERT(__cpp_lib_ranges_as_const == 202207L); +#endif +#else +#ifdef __cpp_lib_ranges_as_const +#error __cpp_lib_ranges_as_const is defined +#endif +#endif + #if _HAS_CXX23 && defined(__cpp_lib_concepts) // TRANSITION, GH-395 #ifndef __cpp_lib_ranges_as_rvalue #error __cpp_lib_ranges_as_rvalue is not defined