diff --git a/stl/inc/ranges b/stl/inc/ranges index 8e5c0cde338..a1dec1351b0 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -41,7 +41,7 @@ namespace ranges { && same_as, sentinel_t>; template - concept _Copy_constructible_object = copy_constructible<_Ty> && is_object_v<_Ty>; + concept _Copy_constructible_object = copy_constructible<_Ty> && _Destructible_object<_Ty>; template concept _Has_arrow = input_iterator<_It> @@ -408,7 +408,6 @@ namespace ranges { private: union { - _Nontrivial_dummy_type _Dummy; _Ty _Val; }; bool _Engaged; @@ -475,7 +474,7 @@ namespace ranges { template class _Defaultabox { // a simplified optional that augments movable types with default-constructibility public: - constexpr _Defaultabox() noexcept : _Dummy{} {} + constexpr _Defaultabox() noexcept {} // clang-format off ~_Defaultabox() requires is_trivially_destructible_v<_Ty> = default; @@ -611,7 +610,6 @@ namespace ranges { private: union { - _Nontrivial_dummy_type _Dummy; _Ty _Val; }; bool _Engaged = false; @@ -654,6 +652,86 @@ namespace ranges { /* [[no_unique_address]] */ _Ty _Value{}; }; + template <_Destructible_object _Ty> + class _Non_propagating_cache { // a simplified optional that resets on copy / move + public: + constexpr _Non_propagating_cache() noexcept {} + + constexpr ~_Non_propagating_cache() { + if (_Engaged) { + _Val.~_Ty(); + } + } + + // clang-format off + ~_Non_propagating_cache() requires is_trivially_destructible_v<_Ty> = default; + // clang-format on + + constexpr _Non_propagating_cache(const _Non_propagating_cache&) noexcept {} + + constexpr _Non_propagating_cache(_Non_propagating_cache&& _Other) noexcept { + if (_Other._Engaged) { + _Other._Val.~_Ty(); + _Other._Engaged = false; + } + } + + constexpr _Non_propagating_cache& operator=(const _Non_propagating_cache& _Other) noexcept { + if (_STD addressof(_Other) == this) { + return *this; + } + + if (_Engaged) { + _Val.~_Ty(); + _Engaged = false; + } + + return *this; + } + + constexpr _Non_propagating_cache& operator=(_Non_propagating_cache&& _Other) noexcept { + if (_Engaged) { + _Val.~_Ty(); + _Engaged = false; + } + + if (_Other._Engaged) { + _Other._Val.~_Ty(); + _Other._Engaged = false; + } + + return *this; + } + + _NODISCARD constexpr _Ty& operator*() noexcept { + _STL_INTERNAL_CHECK(_Engaged); + return _Val; + } + _NODISCARD constexpr const _Ty& operator*() const noexcept { + _STL_INTERNAL_CHECK(_Engaged); + return _Val; + } + + template + constexpr _Ty& _Emplace(_Types&&... _Args) noexcept(is_nothrow_constructible_v<_Ty, _Types...>) { + if (_Engaged) { + _Val.~_Ty(); + _Engaged = false; + } + + _Construct_in_place(_Val, _STD forward<_Types>(_Args)...); + _Engaged = true; + + return _Val; + } + + private: + union { + _Ty _Val; + }; + bool _Engaged = false; + }; + // clang-format off template requires is_object_v<_Ty> @@ -2873,15 +2951,23 @@ namespace ranges { // clang-format off template - requires (view<_Vw> && input_range> - && (is_reference_v> || view>)) + requires view<_Vw> && input_range> class join_view; // clang-format on template class _Join_view_base : public view_interface> { + private: + struct _Cache_wrapper { + template + constexpr _Cache_wrapper(_Not_quite_object::_Construct_tag, const _Iter& _It) noexcept(noexcept(*_It)) + : _Val(*_It) {} + + remove_cv_t> _Val; + }; + protected: - /* [[no_unique_address]] */ _Defaultabox>> _Inner{}; + /* [[no_unique_address]] */ _Non_propagating_cache<_Cache_wrapper> _Inner{}; }; // clang-format off @@ -2890,8 +2976,7 @@ namespace ranges { class _Join_view_base<_Vw> : public view_interface> {}; template - requires (view<_Vw> && input_range> - && (is_reference_v> || view>)) + requires view<_Vw> && input_range> class join_view : public _Join_view_base<_Vw> { // clang-format on private: @@ -2948,20 +3033,19 @@ namespace ranges { /* [[no_unique_address]] */ _Defaultabox<_InnerIter> _Inner{}; // per LWG issue unfiled as of 2021-06-14 _Parent_t* _Parent{}; - constexpr auto& _Update_inner(_InnerRng<_Const> _Range) { + constexpr auto&& _Update_inner() { if constexpr (_Deref_is_glvalue) { - return _Range; + return *_Outer; } else { - _Parent->_Inner = views::all(_STD move(_Range)); - return *_Parent->_Inner; + return _Parent->_Inner._Emplace(_Not_quite_object::_Construct_tag{}, _Outer)._Val; } } constexpr void _Satisfy() { const auto _Last = _RANGES end(_Parent->_Range); for (; _Outer != _Last; ++_Outer) { - auto& _Tmp = _Update_inner(*_Outer); - _Inner = _RANGES begin(_Tmp); + auto&& _Tmp = _Update_inner(); + _Inner = _RANGES begin(_Tmp); if (*_Inner != _RANGES end(_Tmp)) { return; } @@ -2979,7 +3063,7 @@ namespace ranges { if constexpr (_Deref_is_glvalue) { _Last = _RANGES end(*_Outer); } else { - _Last = _RANGES end(*_Parent->_Inner); + _Last = _RANGES end((*_Parent->_Inner)._Val); } _STL_VERIFY(_Inner && *_Inner != _Last, "cannot dereference join_view end iterator"); } @@ -3047,7 +3131,7 @@ namespace ranges { _Satisfy(); } } else { - if (++*_Inner == _RANGES end(*_Parent->_Inner)) { + if (++*_Inner == _RANGES end((*_Parent->_Inner)._Val)) { ++_Outer; _Satisfy(); } diff --git a/stl/inc/xutility b/stl/inc/xutility index 6d3906aae83..7165b62d057 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -323,6 +323,9 @@ concept _Has_member_reference = requires { typename _Ty::reference; }; +template +concept _Destructible_object = is_object_v<_Ty> && destructible<_Ty>; + template struct incrementable_traits {}; @@ -638,6 +641,7 @@ struct iterator_traits : _Iterator_traits_base<_Ty> { template requires is_object_v<_Ty> struct iterator_traits<_Ty*> { + // clang-format on using iterator_concept = contiguous_iterator_tag; using iterator_category = random_access_iterator_tag; using value_type = remove_cv_t<_Ty>; @@ -645,7 +649,6 @@ struct iterator_traits<_Ty*> { using pointer = _Ty*; using reference = _Ty&; }; -// clang-format on namespace ranges { namespace _Iter_move { diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index 84f1a357b7a..b6156383f04 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -249,6 +249,7 @@ // P2259R1 Repairing Input Range Adaptors And counted_iterator // (partially implemented) // P2325R3 Views Should Not Be Required To Be Default Constructible +// P2328R1 join_view Should Join All views Of ranges // P????R? directory_entry::clear_cache() // _HAS_CXX20 indirectly controls: diff --git a/tests/std/tests/P0896R4_views_join/test.cpp b/tests/std/tests/P0896R4_views_join/test.cpp index 5ac3f29951b..7b233f80c21 100644 --- a/tests/std/tests/P0896R4_views_join/test.cpp +++ b/tests/std/tests/P0896R4_views_join/test.cpp @@ -33,8 +33,7 @@ constexpr bool test_one(Outer&& rng, Expected&& expected) { // clang-format off constexpr bool can_test = ranges::viewable_range - && input_range> - && (deref_is_glvalue || ranges::view); + && input_range>; // clang-format on if constexpr (can_test) { @@ -463,6 +462,24 @@ void test_move_only_views() { test_one(move_only_view{input}, expected_ints); } +constexpr array prvalue_input = {{{}, "Hello "sv, {}, "World!"sv, {}}}; + +constexpr auto ToVector(const int val) { + return vector{val + 1}; +} + +constexpr auto ToString(const size_t val) { + return string{prvalue_input[val]}; +} + +struct Immovable { + Immovable() = default; + Immovable(const Immovable&) = delete; + Immovable(Immovable&&) = delete; + Immovable& operator=(const Immovable&) = delete; + Immovable& operator=(Immovable&&) = delete; +}; + int main() { // Validate views constexpr string_view expected = "Hello World!"sv; @@ -473,6 +490,10 @@ int main() { static_assert(test_one(sp, expected)); test_one(sp, expected); } + { // ...copyable rvalue + static_assert(test_one(array{{{}, "Hello "sv, {}, "World!"sv, {}}}, expected)); + test_one(array{{{}, "Hello "sv, {}, "World!"sv, {}}}, expected); + } // ... move-only test_move_only_views(); @@ -504,8 +525,51 @@ int main() { assert(ranges::equal(joined, result)); } + { // P2328 range of prvalue array + static constexpr int result[] = {1, 2, 3, 4, 5}; + auto ToArray = [](const int i) { return array{i + 1}; }; + assert(ranges::equal(views::iota(0, 5) | views::transform(ToArray) | views::join, result)); + static_assert(ranges::equal(views::iota(0, 5) | views::transform(ToArray) | views::join, result)); + } + + { // P2328 range of prvalue vector using global function + static constexpr int result[] = {1, 2, 3, 4, 5}; + assert(ranges::equal(views::iota(0, 5) | views::transform(ToVector) | views::join, result)); +#if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-934264 + static_assert(ranges::equal(views::iota(0, 5) | views::transform(ToVector) | views::join, result)); +#endif // not MSVC + } + + { // P2328 range of prvalue vector using lambda + static constexpr int result[] = {1, 2, 3, 4, 5}; + auto ToVectorLambda = [](const int i) { return vector{i + 1}; }; + assert(ranges::equal(views::iota(0, 5) | views::transform(ToVectorLambda) | views::join, result)); +#if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-934264 + static_assert(ranges::equal(views::iota(0, 5) | views::transform(ToVectorLambda) | views::join, result)); +#endif // not MSVC + } + + { // P2328 range of prvalue string using global function + assert(ranges::equal(views::iota(0u, 5u) | views::transform(ToString) | views::join, expected)); +#if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-934264 + static_assert(ranges::equal(views::iota(0u, 5u) | views::transform(ToString) | views::join, expected)); +#endif // not MSVC + } + + { // P2328 range of prvalue string using lambda + auto ToStringLambda = [](const size_t i) { return string{prvalue_input[i]}; }; + assert(ranges::equal(views::iota(0u, 5u) | views::transform(ToStringLambda) | views::join, expected)); #if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-934264 + static_assert(ranges::equal(views::iota(0u, 5u) | views::transform(ToStringLambda) | views::join, expected)); +#endif // not MSVC + } + + { // Immovable type + auto ToArrayOfImmovable = [](int) { return array{}; }; + assert(ranges::distance(views::iota(0, 2) | views::transform(ToArrayOfImmovable) | views::join) == 6); + static_assert(ranges::distance(views::iota(0, 2) | views::transform(ToArrayOfImmovable) | views::join) == 6); + } + STATIC_ASSERT(instantiation_test()); -#endif // TRANSITION, VSO-934264 instantiation_test(); }