* [PATCH 1/5] libstdc++: Implement P2325 changes to default-constructibilty of views @ 2021-06-17 15:22 Patrick Palka 2021-06-17 15:22 ` [PATCH 2/5] libstdc++: Move ranges algos used by <ranges> into ranges_util.h Patrick Palka ` (4 more replies) 0 siblings, 5 replies; 11+ messages in thread From: Patrick Palka @ 2021-06-17 15:22 UTC (permalink / raw) To: gcc-patches; +Cc: libstdc++, Patrick Palka This implements the wording changes of P2325R3 "Views should not be required to be default constructible". Changes are relatively straightforward, besides perhaps those to __box (which now stands for copyable-box instead of semiregular-box) and __non_propagating_cache. For __box, this patch implements the recommended practice to also avoid std::optional when the boxed type is nothrow_move/copy_constructible. For __non_propagating_cache, now that it's used by split_view::_M_current, we need to add assignment from a value of the underlying type to the subset of the std::optional API implemented for the cache (needed by split_view::begin()). Hence the new __non_propagating_cache::operator= overload. While we're changing __box, this fixes the undesirable list-init in the constuctors of the partial specialization as reported in PR100475 comment #7. libstdc++-v3/ChangeLog: * include/bits/iterator_concepts.h (weakly_incrementable): Remove default_initializable requirement. * include/bits/ranges_base.h (ranges::view): Likewise. * include/bits/ranges_util.h (subrange): Constrain the default ctor. * include/bits/stl_iterator.h (back_insert_iterator): Remove the default ctor. (front_insert_iterator): Likewise. (insert_iterator): Likewise. Remove NSDMIs. (common_iterator): Constrain the default ctor. (counted_iterator): Likewise. * include/bits/stream_iterator.h (ostream_iterator): Remove the default ctor. * include/std/ranges (__detail::__box::operator=): Handle self-assignment in the primary template. (__detail::__box): In the partial specialization: adjust constraints as per P2325. Add specialized operator= for the case when the wrapped type is not copyable. Constrain the default ctor. Avoid list-initialization. (single_view): Constraint the default ctor. (iota_view): Relax semiregular constraint to copyable. Constrain the default ctor. (iota_view::_Iterator): Constraint the default ctor. (basic_istream_view): Remove the default ctor. Remove NSDMIs. Remove redundant checks for empty _M_stream. (basic_istream_view::_Iterator): Likewise. (ref_view): Remove the default ctor. Remove NSDMIs. (ref_view::_Iterator): Constrain the default ctor. (__detail::__non_propagating_cache::operator=): Define overload for assigning from a value of the underlying type. (filter_view): Likewise. (filter_view::_Iterator): Likewise. (transform_view): Likewise. (transform_view::_Iterator): Likewise. (take_view): Likewise. (take_view::_Iterator): Likewise. (take_while_view): Likewise. (take_while_view::_Iterator): Likewise. (drop_while_view): Likewise. (drop_while_view::_Iterator): Likewise. (join_view): Likewise. (split_view::_OuterIter::__current): Adjust after changing the type of _M_current. (split_view::_M_current): Wrap it in a __non_propagating_cache. (split_view::split_view): Constrain the default ctor. (common_view): Constrain the default ctor. (reverse_view): Likewise. (elements_view): Likewise. * include/std/span (enable_view<span<_ElementType, _Extent>>): Define this partial specialization to true unconditionally. * include/std/version (__cpp_lib_ranges): Adjust value. * testsuite/24_iterators/back_insert_iterator/constexpr.cc: Don't attempt to default construct a back_insert_iterator. * testsuite/24_iterators/front_insert_iterator/constexpr.cc: Don't attempt to default construct a front_insert_iterator. * testsuite/24_iterators/insert_iterator/constexpr.cc: Don't attempt to default construct an insert_iterator. * testsuite/24_iterators/ostream_iterator/requirements/constexpr.cc: Remove this test for default constructibility of ostream_iterator. * testsuite/std/ranges/97600.cc: Don't attempt to default construct a basic_istream_view. * testsuite/std/ranges/adaptors/detail/semiregular_box.cc: Rename to ... * testsuite/std/ranges/adaptors/detail/copyable_box.cc: ... this. (test02): Adjust now that __box is copyable-box not semiregular-box. (test03): New test. * testsuite/std/ranges/p2325.cc: New test. * testsuite/std/ranges/single_view.cc (test06): New test. * testsuite/std/ranges/view.cc: Adjust now that view doesn't require default_initializable. --- libstdc++-v3/include/bits/iterator_concepts.h | 3 +- libstdc++-v3/include/bits/ranges_base.h | 3 +- libstdc++-v3/include/bits/ranges_util.h | 2 +- libstdc++-v3/include/bits/stl_iterator.h | 16 +- libstdc++-v3/include/bits/stream_iterator.h | 5 - libstdc++-v3/include/std/ranges | 160 ++++++++++++------ libstdc++-v3/include/std/span | 3 +- libstdc++-v3/include/std/version | 2 +- .../back_insert_iterator/constexpr.cc | 3 +- .../front_insert_iterator/constexpr.cc | 3 +- .../24_iterators/insert_iterator/constexpr.cc | 3 +- .../requirements/constexpr.cc | 24 --- libstdc++-v3/testsuite/std/ranges/97600.cc | 3 +- .../{semiregular_box.cc => copyable_box.cc} | 51 +++++- libstdc++-v3/testsuite/std/ranges/p2325.cc | 155 +++++++++++++++++ .../testsuite/std/ranges/single_view.cc | 15 ++ libstdc++-v3/testsuite/std/ranges/view.cc | 2 +- 17 files changed, 335 insertions(+), 118 deletions(-) delete mode 100644 libstdc++-v3/testsuite/24_iterators/ostream_iterator/requirements/constexpr.cc rename libstdc++-v3/testsuite/std/ranges/adaptors/detail/{semiregular_box.cc => copyable_box.cc} (70%) create mode 100644 libstdc++-v3/testsuite/std/ranges/p2325.cc diff --git a/libstdc++-v3/include/bits/iterator_concepts.h b/libstdc++-v3/include/bits/iterator_concepts.h index 11748e5ed7b..c273056c204 100644 --- a/libstdc++-v3/include/bits/iterator_concepts.h +++ b/libstdc++-v3/include/bits/iterator_concepts.h @@ -594,8 +594,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION /// Requirements on types that can be incremented with ++. template<typename _Iter> - concept weakly_incrementable = default_initializable<_Iter> - && movable<_Iter> + concept weakly_incrementable = movable<_Iter> && requires(_Iter __i) { typename iter_difference_t<_Iter>; diff --git a/libstdc++-v3/include/bits/ranges_base.h b/libstdc++-v3/include/bits/ranges_base.h index 25af4b742a6..9d749c8d9b7 100644 --- a/libstdc++-v3/include/bits/ranges_base.h +++ b/libstdc++-v3/include/bits/ranges_base.h @@ -619,8 +619,7 @@ namespace ranges /// [range.view] The ranges::view concept. template<typename _Tp> concept view - = range<_Tp> && movable<_Tp> && default_initializable<_Tp> - && enable_view<_Tp>; + = range<_Tp> && movable<_Tp> && enable_view<_Tp>; // [range.refinements] diff --git a/libstdc++-v3/include/bits/ranges_util.h b/libstdc++-v3/include/bits/ranges_util.h index dd829ed957f..d7b12b3d985 100644 --- a/libstdc++-v3/include/bits/ranges_util.h +++ b/libstdc++-v3/include/bits/ranges_util.h @@ -241,7 +241,7 @@ namespace ranges [[no_unique_address]] _Size<__size_type> _M_size = {}; public: - subrange() = default; + subrange() requires default_initializable<_It> = default; constexpr subrange(__detail::__convertible_to_non_slicing<_It> auto __i, _Sent __s) diff --git a/libstdc++-v3/include/bits/stl_iterator.h b/libstdc++-v3/include/bits/stl_iterator.h index 8768624b7d1..6ec046b597b 100644 --- a/libstdc++-v3/include/bits/stl_iterator.h +++ b/libstdc++-v3/include/bits/stl_iterator.h @@ -639,8 +639,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION typedef _Container container_type; #if __cplusplus > 201703L using difference_type = ptrdiff_t; - - constexpr back_insert_iterator() noexcept : container(nullptr) { } #endif /// The only way to create this %iterator is with a container. @@ -742,8 +740,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION typedef _Container container_type; #if __cplusplus > 201703L using difference_type = ptrdiff_t; - - constexpr front_insert_iterator() noexcept : container(nullptr) { } #endif /// The only way to create this %iterator is with a container. @@ -843,17 +839,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { #if __cplusplus > 201703L && defined __cpp_lib_concepts using _Iter = std::__detail::__range_iter_t<_Container>; - - protected: - _Container* container = nullptr; - _Iter iter = _Iter(); #else typedef typename _Container::iterator _Iter; - +#endif protected: _Container* container; _Iter iter; -#endif public: /// A nested typedef for the type of whatever container you used. @@ -861,8 +852,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION #if __cplusplus > 201703L && defined __cpp_lib_concepts using difference_type = ptrdiff_t; - - insert_iterator() = default; #endif /** @@ -1740,6 +1729,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION constexpr common_iterator() noexcept(is_nothrow_default_constructible_v<_It>) + requires default_initializable<_It> : _M_it(), _M_index(0) { } @@ -2117,7 +2107,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION // iterator_concept defined in __counted_iter_concept // iterator_category defined in __counted_iter_cat - constexpr counted_iterator() = default; + constexpr counted_iterator() requires default_initializable<_It> = default; constexpr counted_iterator(_It __i, iter_difference_t<_It> __n) diff --git a/libstdc++-v3/include/bits/stream_iterator.h b/libstdc++-v3/include/bits/stream_iterator.h index fd8920b8d01..d07474d4996 100644 --- a/libstdc++-v3/include/bits/stream_iterator.h +++ b/libstdc++-v3/include/bits/stream_iterator.h @@ -192,11 +192,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION const _CharT* _M_string; public: -#if __cplusplus > 201703L - constexpr ostream_iterator() noexcept - : _M_stream(nullptr), _M_string(nullptr) { } -#endif - /// Construct from an ostream. ostream_iterator(ostream_type& __s) : _M_stream(std::__addressof(__s)), _M_string(0) {} diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges index 73c35dafd29..f96adf63d10 100644 --- a/libstdc++-v3/include/std/ranges +++ b/libstdc++-v3/include/std/ranges @@ -113,10 +113,13 @@ namespace ranges noexcept(is_nothrow_copy_constructible_v<_Tp>) requires (!copyable<_Tp>) { - if ((bool)__that) - this->emplace(*__that); - else - this->reset(); + if (this != std::__addressof(__that)) + { + if ((bool)__that) + this->emplace(*__that); + else + this->reset(); + } return *this; } @@ -125,37 +128,42 @@ namespace ranges noexcept(is_nothrow_move_constructible_v<_Tp>) requires (!movable<_Tp>) { - if ((bool)__that) - this->emplace(std::move(*__that)); - else - this->reset(); + if (this != std::__addressof(__that)) + { + if ((bool)__that) + this->emplace(std::move(*__that)); + else + this->reset(); + } return *this; } }; - // For types which are already semiregular, this specialization of the - // semiregular wrapper stores the object directly without going through + // For types which are already copyable, this specialization of the + // copyable wrapper stores the object directly without going through // std::optional. It provides just the subset of the primary template's // API that we currently use. - template<__boxable _Tp> requires semiregular<_Tp> + template<__boxable _Tp> + requires copyable<_Tp> || (is_nothrow_move_constructible_v<_Tp> + && is_nothrow_copy_constructible_v<_Tp>) struct __box<_Tp> { private: [[no_unique_address]] _Tp _M_value = _Tp(); public: - __box() = default; + __box() requires default_initializable<_Tp> = default; constexpr explicit __box(const _Tp& __t) noexcept(is_nothrow_copy_constructible_v<_Tp>) - : _M_value{__t} + : _M_value(__t) { } constexpr explicit __box(_Tp&& __t) noexcept(is_nothrow_move_constructible_v<_Tp>) - : _M_value{std::move(__t)} + : _M_value(std::move(__t)) { } template<typename... _Args> @@ -166,6 +174,38 @@ namespace ranges : _M_value(std::forward<_Args>(__args)...) { } + __box(const __box&) = default; + __box(__box&&) = default; + __box& operator=(const __box&) requires copyable<_Tp> = default; + __box& operator=(__box&&) requires copyable<_Tp> = default; + + // When _Tp is nothrow_copy_constructible but not copy_assignable, + // copy assignment is implemented via destroy-then-copy-construct. + constexpr __box& + operator=(const __box& __that) noexcept + { + static_assert(is_nothrow_copy_constructible_v<_Tp>); + if (this != std::__addressof(__that)) + { + _M_value.~_Tp(); + std::construct_at(std::__addressof(_M_value), *__that); + } + return *this; + } + + // Likewise for move assignment. + constexpr __box& + operator=(__box&& __that) noexcept + { + static_assert(is_nothrow_move_constructible_v<_Tp>); + if (this != std::__addressof(__that)) + { + _M_value.~_Tp(); + std::construct_at(std::__addressof(_M_value), std::move(*__that)); + } + return *this; + } + constexpr bool has_value() const noexcept { return true; }; @@ -193,7 +233,7 @@ namespace ranges class single_view : public view_interface<single_view<_Tp>> { public: - single_view() = default; + single_view() requires default_initializable<_Tp> = default; constexpr explicit single_view(const _Tp& __t) @@ -308,7 +348,7 @@ namespace ranges template<weakly_incrementable _Winc, semiregular _Bound = unreachable_sentinel_t> requires std::__detail::__weakly_eq_cmp_with<_Winc, _Bound> - && semiregular<_Winc> + && copyable<_Winc> class iota_view : public view_interface<iota_view<_Winc, _Bound>> { private: @@ -337,7 +377,7 @@ namespace ranges using value_type = _Winc; using difference_type = __detail::__iota_diff_t<_Winc>; - _Iterator() = default; + _Iterator() requires default_initializable<_Winc> = default; constexpr explicit _Iterator(_Winc __value) @@ -534,7 +574,7 @@ namespace ranges [[no_unique_address]] _Bound _M_bound = _Bound(); public: - iota_view() = default; + iota_view() requires default_initializable<_Winc> = default; constexpr explicit iota_view(_Winc __value) @@ -643,8 +683,6 @@ namespace views : public view_interface<basic_istream_view<_Val, _CharT, _Traits>> { public: - basic_istream_view() = default; - constexpr explicit basic_istream_view(basic_istream<_CharT, _Traits>& __stream) : _M_stream(std::__addressof(__stream)) @@ -653,8 +691,7 @@ namespace views constexpr auto begin() { - if (_M_stream != nullptr) - *_M_stream >> _M_object; + *_M_stream >> _M_object; return _Iterator{this}; } @@ -663,8 +700,8 @@ namespace views { return default_sentinel; } private: - basic_istream<_CharT, _Traits>* _M_stream = nullptr; - _Val _M_object = _Val(); + basic_istream<_CharT, _Traits>* _M_stream; + _Val _M_object; struct _Iterator { @@ -673,8 +710,6 @@ namespace views using difference_type = ptrdiff_t; using value_type = _Val; - _Iterator() = default; - constexpr explicit _Iterator(basic_istream_view* __parent) noexcept : _M_parent(__parent) @@ -688,7 +723,6 @@ namespace views _Iterator& operator++() { - __glibcxx_assert(_M_parent->_M_stream != nullptr); *_M_parent->_M_stream >> _M_parent->_M_object; return *this; } @@ -699,21 +733,18 @@ namespace views _Val& operator*() const - { - __glibcxx_assert(_M_parent->_M_stream != nullptr); - return _M_parent->_M_object; - } + { return _M_parent->_M_object; } friend bool operator==(const _Iterator& __x, default_sentinel_t) { return __x._M_at_end(); } private: - basic_istream_view* _M_parent = nullptr; + basic_istream_view* _M_parent; bool _M_at_end() const - { return _M_parent == nullptr || !*_M_parent->_M_stream; } + { return !*_M_parent->_M_stream; } }; friend _Iterator; @@ -1017,15 +1048,12 @@ namespace views::__adaptor class ref_view : public view_interface<ref_view<_Range>> { private: - _Range* _M_r = nullptr; + _Range* _M_r; static void _S_fun(_Range&); // not defined static void _S_fun(_Range&&) = delete; public: - constexpr - ref_view() noexcept = default; - template<__detail::__not_same_as<ref_view> _Tp> requires convertible_to<_Tp, _Range&> && requires { _S_fun(declval<_Tp>()); } @@ -1205,6 +1233,16 @@ namespace views::__adaptor return *this; } + constexpr __non_propagating_cache& + operator=(_Tp __val) + { + this->_M_reset(); + std::construct_at(std::__addressof(this->_M_payload._M_payload), + std::in_place, std::move(__val)); + this->_M_payload._M_engaged = true; + return *this; + } + constexpr _Tp& operator*() noexcept { return this->_M_get(); } @@ -1382,7 +1420,7 @@ namespace views::__adaptor using value_type = range_value_t<_Vp>; using difference_type = range_difference_t<_Vp>; - _Iterator() = default; + _Iterator() requires default_initializable<_Vp_iter> = default; constexpr _Iterator(filter_view* __parent, _Vp_iter __current) @@ -1494,7 +1532,9 @@ namespace views::__adaptor _Vp _M_base = _Vp(); public: - filter_view() = default; + filter_view() requires (default_initializable<_Vp> + && default_initializable<_Pred>) + = default; constexpr filter_view(_Vp __base, _Pred __pred) @@ -1643,7 +1683,7 @@ namespace views::__adaptor = remove_cvref_t<invoke_result_t<_Fp&, range_reference_t<_Base>>>; using difference_type = range_difference_t<_Base>; - _Iterator() = default; + _Iterator() requires default_initializable<_Base_iter> = default; constexpr _Iterator(_Parent* __parent, _Base_iter __current) @@ -1858,7 +1898,9 @@ namespace views::__adaptor _Vp _M_base = _Vp(); public: - transform_view() = default; + transform_view() requires (default_initializable<_Vp> + && default_initializable<_Fp>) + = default; constexpr transform_view(_Vp __base, _Fp __fun) @@ -1993,7 +2035,7 @@ namespace views::__adaptor _Vp _M_base = _Vp(); public: - take_view() = default; + take_view() requires default_initializable<_Vp> = default; constexpr take_view(_Vp base, range_difference_t<_Vp> __count) @@ -2177,7 +2219,9 @@ namespace views::__adaptor _Vp _M_base = _Vp(); public: - take_while_view() = default; + take_while_view() requires (default_initializable<_Vp> + && default_initializable<_Pred>) + = default; constexpr take_while_view(_Vp base, _Pred __pred) @@ -2265,7 +2309,7 @@ namespace views::__adaptor _M_cached_begin; public: - drop_view() = default; + drop_view() requires default_initializable<_Vp> = default; constexpr drop_view(_Vp __base, range_difference_t<_Vp> __count) @@ -2381,7 +2425,9 @@ namespace views::__adaptor _Vp _M_base = _Vp(); public: - drop_while_view() = default; + drop_while_view() requires (default_initializable<_Vp> + && default_initializable<_Pred>) + = default; constexpr drop_while_view(_Vp __base, _Pred __pred) @@ -2571,7 +2617,9 @@ namespace views::__adaptor = common_type_t<range_difference_t<_Base>, range_difference_t<range_reference_t<_Base>>>; - _Iterator() = default; + _Iterator() requires (default_initializable<_Outer_iter> + && default_initializable<_Inner_iter>) + = default; constexpr _Iterator(_Parent* __parent, _Outer_iter __outer) @@ -2724,7 +2772,7 @@ namespace views::__adaptor _Vp _M_base = _Vp(); public: - join_view() = default; + join_view() requires default_initializable<_Vp> = default; constexpr explicit join_view(_Vp __base) @@ -2891,7 +2939,7 @@ namespace views::__adaptor if constexpr (forward_range<_Vp>) return _M_current; else - return _M_parent->_M_current; + return *_M_parent->_M_current; } constexpr auto& @@ -2900,7 +2948,7 @@ namespace views::__adaptor if constexpr (forward_range<_Vp>) return _M_current; else - return _M_parent->_M_current; + return *_M_parent->_M_current; } _Parent* _M_parent = nullptr; @@ -3146,12 +3194,14 @@ namespace views::__adaptor // XXX: _M_current is "present only if !forward_range<V>" [[no_unique_address]] __detail::__maybe_present_t<!forward_range<_Vp>, - iterator_t<_Vp>> _M_current; + __detail::__non_propagating_cache<iterator_t<_Vp>>> _M_current; _Vp _M_base = _Vp(); public: - split_view() = default; + split_view() requires (default_initializable<_Vp> + && default_initializable<_Pattern>) + = default; constexpr split_view(_Vp __base, _Pattern __pattern) @@ -3282,7 +3332,7 @@ namespace views::__adaptor _Vp _M_base = _Vp(); public: - common_view() = default; + common_view() requires default_initializable<_Vp> = default; constexpr explicit common_view(_Vp __r) @@ -3413,7 +3463,7 @@ namespace views::__adaptor _Vp _M_base = _Vp(); public: - reverse_view() = default; + reverse_view() requires default_initializable<_Vp> = default; constexpr explicit reverse_view(_Vp __r) @@ -3555,7 +3605,7 @@ namespace views::__adaptor class elements_view : public view_interface<elements_view<_Vp, _Nm>> { public: - elements_view() = default; + elements_view() requires default_initializable<_Vp> = default; constexpr explicit elements_view(_Vp base) @@ -3676,7 +3726,7 @@ namespace views::__adaptor = remove_cvref_t<tuple_element_t<_Nm, range_value_t<_Base>>>; using difference_type = range_difference_t<_Base>; - _Iterator() = default; + _Iterator() requires default_initializable<iterator_t<_Base>> = default; constexpr explicit _Iterator(iterator_t<_Base> current) diff --git a/libstdc++-v3/include/std/span b/libstdc++-v3/include/std/span index 09bdcd69afb..63f0a8f6279 100644 --- a/libstdc++-v3/include/std/span +++ b/libstdc++-v3/include/std/span @@ -447,8 +447,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION // Opt-in to view concept template<typename _ElementType, size_t _Extent> inline constexpr bool - enable_view<span<_ElementType, _Extent>> - = _Extent == 0 || _Extent == dynamic_extent; + enable_view<span<_ElementType, _Extent>> = true; } _GLIBCXX_END_NAMESPACE_VERSION } // namespace std diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version index 8d0b2b95f34..e3ab9b4c7c0 100644 --- a/libstdc++-v3/include/std/version +++ b/libstdc++-v3/include/std/version @@ -240,7 +240,7 @@ #define __cpp_lib_math_constants 201907L #define __cpp_lib_polymorphic_allocator 201902L #if __cpp_lib_concepts -# define __cpp_lib_ranges 201911L +# define __cpp_lib_ranges 202106L #endif #if __cpp_lib_atomic_wait || _GLIBCXX_HAVE_POSIX_SEMAPHORE # define __cpp_lib_semaphore 201907L diff --git a/libstdc++-v3/testsuite/24_iterators/back_insert_iterator/constexpr.cc b/libstdc++-v3/testsuite/24_iterators/back_insert_iterator/constexpr.cc index bef2289ba79..27acd071df1 100644 --- a/libstdc++-v3/testsuite/24_iterators/back_insert_iterator/constexpr.cc +++ b/libstdc++-v3/testsuite/24_iterators/back_insert_iterator/constexpr.cc @@ -42,8 +42,7 @@ constexpr bool test01() { container c; - std::back_insert_iterator<container> iter; - iter = std::back_inserter(c); + std::back_insert_iterator<container> iter = std::back_inserter(c); *iter++ = 1; int i = 2; *iter = i; diff --git a/libstdc++-v3/testsuite/24_iterators/front_insert_iterator/constexpr.cc b/libstdc++-v3/testsuite/24_iterators/front_insert_iterator/constexpr.cc index 7b4c990b107..cff7f6a4524 100644 --- a/libstdc++-v3/testsuite/24_iterators/front_insert_iterator/constexpr.cc +++ b/libstdc++-v3/testsuite/24_iterators/front_insert_iterator/constexpr.cc @@ -42,8 +42,7 @@ constexpr bool test01() { container c; - std::front_insert_iterator<container> iter; - iter = std::front_inserter(c); + std::front_insert_iterator<container> iter = std::front_inserter(c); *iter++ = 1; int i = 2; *iter = i; diff --git a/libstdc++-v3/testsuite/24_iterators/insert_iterator/constexpr.cc b/libstdc++-v3/testsuite/24_iterators/insert_iterator/constexpr.cc index e74df3eb5d5..e326b01d534 100644 --- a/libstdc++-v3/testsuite/24_iterators/insert_iterator/constexpr.cc +++ b/libstdc++-v3/testsuite/24_iterators/insert_iterator/constexpr.cc @@ -51,8 +51,7 @@ constexpr bool test01() { container c; - std::insert_iterator<container> iter; - iter = std::inserter(c, c.begin()); + std::insert_iterator<container> iter = std::inserter(c, c.begin()); *iter++ = 1; int i = 2; *iter = i; diff --git a/libstdc++-v3/testsuite/24_iterators/ostream_iterator/requirements/constexpr.cc b/libstdc++-v3/testsuite/24_iterators/ostream_iterator/requirements/constexpr.cc deleted file mode 100644 index 4edaaa8aebb..00000000000 --- a/libstdc++-v3/testsuite/24_iterators/ostream_iterator/requirements/constexpr.cc +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (C) 2019-2021 Free Software Foundation, Inc. -// -// This file is part of the GNU ISO C++ Library. This library is free -// software; you can redistribute it and/or modify it under the -// terms of the GNU General Public License as published by the -// Free Software Foundation; either version 3, or (at your option) -// any later version. - -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License along -// with this library; see the file COPYING3. If not see -// <http://www.gnu.org/licenses/>. - -// { dg-options "-std=gnu++2a" } -// { dg-do compile { target c++2a } } - -#include <iterator> - -constexpr std::ostream_iterator<int> iter1; -constexpr std::ostream_iterator<int> iter2{}; diff --git a/libstdc++-v3/testsuite/std/ranges/97600.cc b/libstdc++-v3/testsuite/std/ranges/97600.cc index 7435de022cd..c642b9d22d0 100644 --- a/libstdc++-v3/testsuite/std/ranges/97600.cc +++ b/libstdc++-v3/testsuite/std/ranges/97600.cc @@ -24,9 +24,8 @@ #include <ranges> void -test01() +test01(std::ranges::basic_istream_view<int, char, std::char_traits<char>> v) { - std::ranges::basic_istream_view<int, char, std::char_traits<char>> v; v.begin(); static_assert(std::ranges::range<decltype(v)>); } diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/detail/semiregular_box.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/detail/copyable_box.cc similarity index 70% rename from libstdc++-v3/testsuite/std/ranges/adaptors/detail/semiregular_box.cc rename to libstdc++-v3/testsuite/std/ranges/adaptors/detail/copyable_box.cc index ed694e04fd1..fa6d4d56816 100644 --- a/libstdc++-v3/testsuite/std/ranges/adaptors/detail/semiregular_box.cc +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/detail/copyable_box.cc @@ -82,9 +82,10 @@ test01() } static_assert(test01()); -template<bool make_semiregular> +template<bool make_copyable> struct A { - A() requires make_semiregular; + A(const A&) = default; + A& operator=(const A&) requires make_copyable; A(int, int); A(std::initializer_list<int>) = delete; }; @@ -93,9 +94,51 @@ void test02() { // PR libstdc++/100475 - static_assert(std::semiregular<A<true>>); + static_assert(std::copyable<A<true>>); __box<A<true>> x2(std::in_place, 0, 0); - static_assert(!std::semiregular<A<false>>); + static_assert(!std::copyable<A<false>>); __box<A<false>> x1(std::in_place, 0, 0); } + +constexpr bool +test03() +{ + // Verify correctness of the non-defaulted operator= for the partial + // specialization of __box. + struct B { + constexpr B(int* p) : p(p) { } + constexpr ~B() { ++*p; }; + B(const B&) = default; + B& operator=(const B&) = delete; + int* p; + }; + static_assert(!std::copyable<B>); + static_assert(std::is_nothrow_copy_constructible_v<B>); + static_assert(sizeof(__box<B>) == sizeof(B)); + + int m = 0; + __box<B> x(std::in_place, &m); + __glibcxx_assert(m == 0); + x = x; + __glibcxx_assert(m == 0); + x = std::move(x); + __glibcxx_assert(m == 0); + + int n = 0; + __box<B> y(std::in_place, &n); + auto z = x; + x = y; + __glibcxx_assert(m == 1); + __glibcxx_assert(n == 0); + __glibcxx_assert(x->p == &n); + __glibcxx_assert(y->p == &n); + y = std::move(z); + __glibcxx_assert(m == 1); + __glibcxx_assert(n == 1); + __glibcxx_assert(y->p == &m); + __glibcxx_assert(z->p == &m); + + return true; +} +static_assert(test03()); diff --git a/libstdc++-v3/testsuite/std/ranges/p2325.cc b/libstdc++-v3/testsuite/std/ranges/p2325.cc new file mode 100644 index 00000000000..df6cde29e4d --- /dev/null +++ b/libstdc++-v3/testsuite/std/ranges/p2325.cc @@ -0,0 +1,155 @@ +// { dg-options "-std=gnu++20" } +// { dg-do compile { target c++20 } } +// P2325R3 "Views should not be required to be default constructible" + +#include <ranges> +#include <iterator> +#include <span> +#include <sstream> +#include <vector> +#include <testsuite_iterators.h> + +using namespace std; + +template<default_initializable T> void f(); +template<typename T> requires weakly_incrementable<T> || ranges::view<T> void f(); + +void +test01() +{ + // Verify neither std::weakly_incrementable nor ranges::view require + // default_initializable. + f<int>(); // { dg-error "ambiguous" } +} + +void +test02() +{ + // Verify these iterators are not default constructible. + static_assert(!default_initializable<insert_iterator<vector<int>>>); + static_assert(!default_initializable<front_insert_iterator<vector<int>>>); + static_assert(!default_initializable<back_insert_iterator<vector<int>>>); + static_assert(!default_initializable<ostream_iterator<int>>); + + using iter = ostream_iterator<int>; + + // Verify common_iterator is conditionally default constructible. + static_assert(!default_initializable<common_iterator<iter, unreachable_sentinel_t>>); + static_assert(default_initializable<common_iterator<int*, unreachable_sentinel_t>>); + + // Verify counted_iterator is conditionally default constructible. + static_assert(!default_initializable<counted_iterator<iter>>); + static_assert(default_initializable<counted_iterator<int*>>); +} + +void +test03() +{ + using iter = ostream_iterator<int>; + + // Verify iota_view is conditionally default constructible. + static_assert(!default_initializable<ranges::iota_view<iter>>); + static_assert(!default_initializable<decltype(declval<ranges::iota_view<iter>>().begin())>); + static_assert(default_initializable<ranges::iota_view<int>>); + static_assert(default_initializable<decltype(declval<ranges::iota_view<int>>().begin())>); + + // Verify subrange is conditionally default constructible. + static_assert(!default_initializable<ranges::subrange<iter, unreachable_sentinel_t>>); + static_assert(default_initializable<ranges::subrange<int*, unreachable_sentinel_t>>); + + // Verify single_view is conditionally default constructible. + static_assert(!default_initializable<ranges::single_view<iter>>); + static_assert(default_initializable<ranges::single_view<int*>>); +} + +void +test04() +{ + // Verify basic_istream_view is not default constructible. + using type = ranges::basic_istream_view<int, char, char_traits<char>>; + static_assert(!default_initializable<type>); + static_assert(!default_initializable<decltype(declval<type>().begin())>); +} + +void +test05() +{ + // Verify ref_view is not default constructible. + static_assert(!default_initializable<ranges::ref_view<int[5]>>); +} + +template<auto adaptor> +void +test06() +{ + auto f1 = [] (auto) { return true; }; + auto f2 = [i=0] (auto) { return true; }; + static_assert(default_initializable<decltype(views::single(0) | adaptor(f1))>); + static_assert(!default_initializable<decltype(views::single(0) | adaptor(f2))>); + + struct S { S() = delete; }; + static_assert(!default_initializable<decltype(views::single(declval<S>()) | adaptor(f1))>); + static_assert(!default_initializable<decltype(views::single(declval<S>()) | adaptor(f2))>); +} + +// Verify filter_view, transform_view, take_while_view and drop_while_view are +// conditionally default constructible. +template void test06<views::filter>(); +template void test06<views::transform>(); +template void test06<views::take_while>(); +template void test06<views::drop_while>(); + +void +test07() +{ + // Verify join_view is conditionally default constructible. + struct S { S() = delete; }; + using type1 = ranges::join_view<ranges::single_view<ranges::single_view<S>>>; + static_assert(!default_initializable<type1>); + using type2 = ranges::join_view<ranges::single_view<ranges::single_view<int>>>; + static_assert(default_initializable<type2>); +} + +void +test08() +{ + // Verify split_view is conditionally default constructible. + using type1 = ranges::split_view<ranges::ref_view<int[2]>, ranges::single_view<int>>; + static_assert(!default_initializable<type1>); + using type2 = ranges::split_view<ranges::single_view<int>, ranges::ref_view<int[2]>>; + static_assert(!default_initializable<type2>); + using type3 = ranges::split_view<ranges::ref_view<int[2]>, ranges::ref_view<int[2]>>; + static_assert(!default_initializable<type3>); + using type4 = ranges::split_view<ranges::single_view<int>, ranges::single_view<int>>; + static_assert(default_initializable<type4>); +} + +void +test09() +{ + // Verify common_view is conditionally default constructible. + using type1 = ranges::common_view<ranges::iota_view<ostream_iterator<int>>>; + static_assert(!default_initializable<type1>); + using type2 = ranges::common_view<ranges::iota_view<int*>>; + static_assert(default_initializable<type2>); +} + +void +test10() +{ + // Verify reverse_view is conditionally default constructible. + using type1 = ranges::reverse_view<ranges::ref_view<int[2]>>; + static_assert(!default_initializable<type1>); + using type2 = ranges::reverse_view<ranges::single_view<int>>; + static_assert(default_initializable<type2>); +} + +void +test11() +{ + // Verify elements_view is conditionally default constructible. + using type1 = ranges::elements_view<ranges::ref_view<pair<int,int>[2]>, 0>; + static_assert(!default_initializable<type1>); + using type2 = ranges::elements_view<ranges::single_view<pair<int,int>>, 0>; + static_assert(default_initializable<type2>); +} diff --git a/libstdc++-v3/testsuite/std/ranges/single_view.cc b/libstdc++-v3/testsuite/std/ranges/single_view.cc index c036fc8976a..f1d8e103715 100644 --- a/libstdc++-v3/testsuite/std/ranges/single_view.cc +++ b/libstdc++-v3/testsuite/std/ranges/single_view.cc @@ -96,6 +96,20 @@ test05() static_assert(noexcept(cs.empty())); // view_interface::empty() } +void +test06() +{ + // PR libstdc++/100475 comment #7 + struct S { + S() = default; + S(std::initializer_list<S>) = delete; + S(const S&) {} + }; + S obj; + auto x = std::views::single(obj); + auto y = std::views::single(std::move(obj)); +} + int main() { test01(); @@ -103,4 +117,5 @@ int main() test03(); test04(); test05(); + test06(); } diff --git a/libstdc++-v3/testsuite/std/ranges/view.cc b/libstdc++-v3/testsuite/std/ranges/view.cc index d8972ab3e46..dd8258220ed 100644 --- a/libstdc++-v3/testsuite/std/ranges/view.cc +++ b/libstdc++-v3/testsuite/std/ranges/view.cc @@ -31,7 +31,7 @@ static_assert(std::ranges::view<std::span<int>>); static_assert(std::ranges::view<std::span<int, 0>>); -static_assert(!std::ranges::view<std::span<int, 1>>); +static_assert(std::ranges::view<std::span<int, 1>>); // Changed with P2325R3 static_assert(std::ranges::view<std::string_view>); static_assert(std::ranges::view<std::experimental::string_view>); -- 2.32.0.93.g670b81a890 ^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 2/5] libstdc++: Move ranges algos used by <ranges> into ranges_util.h 2021-06-17 15:22 [PATCH 1/5] libstdc++: Implement P2325 changes to default-constructibilty of views Patrick Palka @ 2021-06-17 15:22 ` Patrick Palka 2021-06-17 18:14 ` Jonathan Wakely 2021-06-17 15:22 ` [PATCH 3/5] libstdc++: Rename views::split to views::lazy_split as per P2210 Patrick Palka ` (3 subsequent siblings) 4 siblings, 1 reply; 11+ messages in thread From: Patrick Palka @ 2021-06-17 15:22 UTC (permalink / raw) To: gcc-patches; +Cc: libstdc++, Patrick Palka The <ranges> header defines simplified copies of some ranges algorithms in order to avoid including the entirety of ranges_algo.h. A subsequent patch is going to want to use ranges::search in <ranges> as well, but that algorithm is more complicated compared to the other copied ones. So rather than additionally copying ranges::search into <ranges>, this patch splits out all the ranges algos used by <ranges> (including ranges::search) from ranges_algo.h to ranges_util.h, and deletes the simplified copies in <ranges>. This seems like the best place for these algorithms, as ranges_util.h is included only from <ranges> and ranges_algo.h. libstdc++-v3/ChangeLog: * include/bits/ranges_algo.h (__find_fn, find, __find_if_fn) (find_if, __find_if_not_fn, find_if_not, _in_in_result) (__mismatch_fn, mismatch, __search_fn, search): Move to ... * include/bits/ranges_util.h: ... here. * include/std/ranges (__detail::find, __detail::find_if) (__detail::find_if_not, __detail::mismatch): Remove. (filter_view): Use ranges::find_if instead. (drop_while_view): Use ranges::find_if_not instead. (split_view): Use ranges::find and ranges::mismatch instead. --- libstdc++-v3/include/bits/ranges_algo.h | 215 +---------------------- libstdc++-v3/include/bits/ranges_util.h | 219 ++++++++++++++++++++++++ libstdc++-v3/include/std/ranges | 72 ++------ 3 files changed, 233 insertions(+), 273 deletions(-) diff --git a/libstdc++-v3/include/bits/ranges_algo.h b/libstdc++-v3/include/bits/ranges_algo.h index ecf1378742d..9eeebff6525 100644 --- a/libstdc++-v3/include/bits/ranges_algo.h +++ b/libstdc++-v3/include/bits/ranges_algo.h @@ -234,91 +234,7 @@ namespace ranges inline constexpr __for_each_n_fn for_each_n{}; - struct __find_fn - { - template<input_iterator _Iter, sentinel_for<_Iter> _Sent, typename _Tp, - typename _Proj = identity> - requires indirect_binary_predicate<ranges::equal_to, - projected<_Iter, _Proj>, const _Tp*> - constexpr _Iter - operator()(_Iter __first, _Sent __last, - const _Tp& __value, _Proj __proj = {}) const - { - while (__first != __last - && !(std::__invoke(__proj, *__first) == __value)) - ++__first; - return __first; - } - - template<input_range _Range, typename _Tp, typename _Proj = identity> - requires indirect_binary_predicate<ranges::equal_to, - projected<iterator_t<_Range>, _Proj>, - const _Tp*> - constexpr borrowed_iterator_t<_Range> - operator()(_Range&& __r, const _Tp& __value, _Proj __proj = {}) const - { - return (*this)(ranges::begin(__r), ranges::end(__r), - __value, std::move(__proj)); - } - }; - - inline constexpr __find_fn find{}; - - struct __find_if_fn - { - template<input_iterator _Iter, sentinel_for<_Iter> _Sent, - typename _Proj = identity, - indirect_unary_predicate<projected<_Iter, _Proj>> _Pred> - constexpr _Iter - operator()(_Iter __first, _Sent __last, - _Pred __pred, _Proj __proj = {}) const - { - while (__first != __last - && !(bool)std::__invoke(__pred, std::__invoke(__proj, *__first))) - ++__first; - return __first; - } - - template<input_range _Range, typename _Proj = identity, - indirect_unary_predicate<projected<iterator_t<_Range>, _Proj>> - _Pred> - constexpr borrowed_iterator_t<_Range> - operator()(_Range&& __r, _Pred __pred, _Proj __proj = {}) const - { - return (*this)(ranges::begin(__r), ranges::end(__r), - std::move(__pred), std::move(__proj)); - } - }; - - inline constexpr __find_if_fn find_if{}; - - struct __find_if_not_fn - { - template<input_iterator _Iter, sentinel_for<_Iter> _Sent, - typename _Proj = identity, - indirect_unary_predicate<projected<_Iter, _Proj>> _Pred> - constexpr _Iter - operator()(_Iter __first, _Sent __last, - _Pred __pred, _Proj __proj = {}) const - { - while (__first != __last - && (bool)std::__invoke(__pred, std::__invoke(__proj, *__first))) - ++__first; - return __first; - } - - template<input_range _Range, typename _Proj = identity, - indirect_unary_predicate<projected<iterator_t<_Range>, _Proj>> - _Pred> - constexpr borrowed_iterator_t<_Range> - operator()(_Range&& __r, _Pred __pred, _Proj __proj = {}) const - { - return (*this)(ranges::begin(__r), ranges::end(__r), - std::move(__pred), std::move(__proj)); - } - }; - - inline constexpr __find_if_not_fn find_if_not{}; + // find, find_if and find_if_not are defined in <bits/ranges_util.h>. struct __find_first_of_fn { @@ -421,134 +337,7 @@ namespace ranges inline constexpr __count_if_fn count_if{}; - template<typename _Iter1, typename _Iter2> - struct in_in_result - { - [[no_unique_address]] _Iter1 in1; - [[no_unique_address]] _Iter2 in2; - - template<typename _IIter1, typename _IIter2> - requires convertible_to<const _Iter1&, _IIter1> - && convertible_to<const _Iter2&, _IIter2> - constexpr - operator in_in_result<_IIter1, _IIter2>() const & - { return {in1, in2}; } - - template<typename _IIter1, typename _IIter2> - requires convertible_to<_Iter1, _IIter1> - && convertible_to<_Iter2, _IIter2> - constexpr - operator in_in_result<_IIter1, _IIter2>() && - { return {std::move(in1), std::move(in2)}; } - }; - - template<typename _Iter1, typename _Iter2> - using mismatch_result = in_in_result<_Iter1, _Iter2>; - - struct __mismatch_fn - { - template<input_iterator _Iter1, sentinel_for<_Iter1> _Sent1, - input_iterator _Iter2, sentinel_for<_Iter2> _Sent2, - typename _Pred = ranges::equal_to, - typename _Proj1 = identity, typename _Proj2 = identity> - requires indirectly_comparable<_Iter1, _Iter2, _Pred, _Proj1, _Proj2> - constexpr mismatch_result<_Iter1, _Iter2> - operator()(_Iter1 __first1, _Sent1 __last1, - _Iter2 __first2, _Sent2 __last2, _Pred __pred = {}, - _Proj1 __proj1 = {}, _Proj2 __proj2 = {}) const - { - while (__first1 != __last1 && __first2 != __last2 - && (bool)std::__invoke(__pred, - std::__invoke(__proj1, *__first1), - std::__invoke(__proj2, *__first2))) - { - ++__first1; - ++__first2; - } - return { std::move(__first1), std::move(__first2) }; - } - - template<input_range _Range1, input_range _Range2, - typename _Pred = ranges::equal_to, - typename _Proj1 = identity, typename _Proj2 = identity> - requires indirectly_comparable<iterator_t<_Range1>, iterator_t<_Range2>, - _Pred, _Proj1, _Proj2> - constexpr mismatch_result<iterator_t<_Range1>, iterator_t<_Range2>> - operator()(_Range1&& __r1, _Range2&& __r2, _Pred __pred = {}, - _Proj1 __proj1 = {}, _Proj2 __proj2 = {}) const - { - return (*this)(ranges::begin(__r1), ranges::end(__r1), - ranges::begin(__r2), ranges::end(__r2), - std::move(__pred), - std::move(__proj1), std::move(__proj2)); - } - }; - - inline constexpr __mismatch_fn mismatch{}; - - struct __search_fn - { - template<forward_iterator _Iter1, sentinel_for<_Iter1> _Sent1, - forward_iterator _Iter2, sentinel_for<_Iter2> _Sent2, - typename _Pred = ranges::equal_to, - typename _Proj1 = identity, typename _Proj2 = identity> - requires indirectly_comparable<_Iter1, _Iter2, _Pred, _Proj1, _Proj2> - constexpr subrange<_Iter1> - operator()(_Iter1 __first1, _Sent1 __last1, - _Iter2 __first2, _Sent2 __last2, _Pred __pred = {}, - _Proj1 __proj1 = {}, _Proj2 __proj2 = {}) const - { - if (__first1 == __last1 || __first2 == __last2) - return {__first1, __first1}; - - for (;;) - { - for (;;) - { - if (__first1 == __last1) - return {__first1, __first1}; - if (std::__invoke(__pred, - std::__invoke(__proj1, *__first1), - std::__invoke(__proj2, *__first2))) - break; - ++__first1; - } - auto __cur1 = __first1; - auto __cur2 = __first2; - for (;;) - { - if (++__cur2 == __last2) - return {__first1, ++__cur1}; - if (++__cur1 == __last1) - return {__cur1, __cur1}; - if (!(bool)std::__invoke(__pred, - std::__invoke(__proj1, *__cur1), - std::__invoke(__proj2, *__cur2))) - { - ++__first1; - break; - } - } - } - } - - template<forward_range _Range1, forward_range _Range2, - typename _Pred = ranges::equal_to, - typename _Proj1 = identity, typename _Proj2 = identity> - requires indirectly_comparable<iterator_t<_Range1>, iterator_t<_Range2>, - _Pred, _Proj1, _Proj2> - constexpr borrowed_subrange_t<_Range1> - operator()(_Range1&& __r1, _Range2&& __r2, _Pred __pred = {}, - _Proj1 __proj1 = {}, _Proj2 __proj2 = {}) const - { - return (*this)(ranges::begin(__r1), ranges::end(__r1), - ranges::begin(__r2), ranges::end(__r2), - std::move(__pred), - std::move(__proj1), std::move(__proj2)); - } - }; - - inline constexpr __search_fn search{}; + // in_in_result, mismatch and search are defined in <bits/ranges_util.h>. struct __search_n_fn { diff --git a/libstdc++-v3/include/bits/ranges_util.h b/libstdc++-v3/include/bits/ranges_util.h index d7b12b3d985..9a07079ac13 100644 --- a/libstdc++-v3/include/bits/ranges_util.h +++ b/libstdc++-v3/include/bits/ranges_util.h @@ -420,7 +420,226 @@ namespace ranges using borrowed_subrange_t = conditional_t<borrowed_range<_Range>, subrange<iterator_t<_Range>>, dangling>; +} // namespace ranges + +// The following ranges algorithms are used by <ranges>, and are defined here +// so that <ranges> can avoid including all of <bits/ranges_algo.h>. +namespace ranges +{ + struct __find_fn + { + template<input_iterator _Iter, sentinel_for<_Iter> _Sent, typename _Tp, + typename _Proj = identity> + requires indirect_binary_predicate<ranges::equal_to, + projected<_Iter, _Proj>, const _Tp*> + constexpr _Iter + operator()(_Iter __first, _Sent __last, + const _Tp& __value, _Proj __proj = {}) const + { + while (__first != __last + && !(std::__invoke(__proj, *__first) == __value)) + ++__first; + return __first; + } + + template<input_range _Range, typename _Tp, typename _Proj = identity> + requires indirect_binary_predicate<ranges::equal_to, + projected<iterator_t<_Range>, _Proj>, + const _Tp*> + constexpr borrowed_iterator_t<_Range> + operator()(_Range&& __r, const _Tp& __value, _Proj __proj = {}) const + { + return (*this)(ranges::begin(__r), ranges::end(__r), + __value, std::move(__proj)); + } + }; + + inline constexpr __find_fn find{}; + + struct __find_if_fn + { + template<input_iterator _Iter, sentinel_for<_Iter> _Sent, + typename _Proj = identity, + indirect_unary_predicate<projected<_Iter, _Proj>> _Pred> + constexpr _Iter + operator()(_Iter __first, _Sent __last, + _Pred __pred, _Proj __proj = {}) const + { + while (__first != __last + && !(bool)std::__invoke(__pred, std::__invoke(__proj, *__first))) + ++__first; + return __first; + } + + template<input_range _Range, typename _Proj = identity, + indirect_unary_predicate<projected<iterator_t<_Range>, _Proj>> + _Pred> + constexpr borrowed_iterator_t<_Range> + operator()(_Range&& __r, _Pred __pred, _Proj __proj = {}) const + { + return (*this)(ranges::begin(__r), ranges::end(__r), + std::move(__pred), std::move(__proj)); + } + }; + + inline constexpr __find_if_fn find_if{}; + + struct __find_if_not_fn + { + template<input_iterator _Iter, sentinel_for<_Iter> _Sent, + typename _Proj = identity, + indirect_unary_predicate<projected<_Iter, _Proj>> _Pred> + constexpr _Iter + operator()(_Iter __first, _Sent __last, + _Pred __pred, _Proj __proj = {}) const + { + while (__first != __last + && (bool)std::__invoke(__pred, std::__invoke(__proj, *__first))) + ++__first; + return __first; + } + + template<input_range _Range, typename _Proj = identity, + indirect_unary_predicate<projected<iterator_t<_Range>, _Proj>> + _Pred> + constexpr borrowed_iterator_t<_Range> + operator()(_Range&& __r, _Pred __pred, _Proj __proj = {}) const + { + return (*this)(ranges::begin(__r), ranges::end(__r), + std::move(__pred), std::move(__proj)); + } + }; + + inline constexpr __find_if_not_fn find_if_not{}; + + template<typename _Iter1, typename _Iter2> + struct in_in_result + { + [[no_unique_address]] _Iter1 in1; + [[no_unique_address]] _Iter2 in2; + + template<typename _IIter1, typename _IIter2> + requires convertible_to<const _Iter1&, _IIter1> + && convertible_to<const _Iter2&, _IIter2> + constexpr + operator in_in_result<_IIter1, _IIter2>() const & + { return {in1, in2}; } + + template<typename _IIter1, typename _IIter2> + requires convertible_to<_Iter1, _IIter1> + && convertible_to<_Iter2, _IIter2> + constexpr + operator in_in_result<_IIter1, _IIter2>() && + { return {std::move(in1), std::move(in2)}; } + }; + + template<typename _Iter1, typename _Iter2> + using mismatch_result = in_in_result<_Iter1, _Iter2>; + + struct __mismatch_fn + { + template<input_iterator _Iter1, sentinel_for<_Iter1> _Sent1, + input_iterator _Iter2, sentinel_for<_Iter2> _Sent2, + typename _Pred = ranges::equal_to, + typename _Proj1 = identity, typename _Proj2 = identity> + requires indirectly_comparable<_Iter1, _Iter2, _Pred, _Proj1, _Proj2> + constexpr mismatch_result<_Iter1, _Iter2> + operator()(_Iter1 __first1, _Sent1 __last1, + _Iter2 __first2, _Sent2 __last2, _Pred __pred = {}, + _Proj1 __proj1 = {}, _Proj2 __proj2 = {}) const + { + while (__first1 != __last1 && __first2 != __last2 + && (bool)std::__invoke(__pred, + std::__invoke(__proj1, *__first1), + std::__invoke(__proj2, *__first2))) + { + ++__first1; + ++__first2; + } + return { std::move(__first1), std::move(__first2) }; + } + + template<input_range _Range1, input_range _Range2, + typename _Pred = ranges::equal_to, + typename _Proj1 = identity, typename _Proj2 = identity> + requires indirectly_comparable<iterator_t<_Range1>, iterator_t<_Range2>, + _Pred, _Proj1, _Proj2> + constexpr mismatch_result<iterator_t<_Range1>, iterator_t<_Range2>> + operator()(_Range1&& __r1, _Range2&& __r2, _Pred __pred = {}, + _Proj1 __proj1 = {}, _Proj2 __proj2 = {}) const + { + return (*this)(ranges::begin(__r1), ranges::end(__r1), + ranges::begin(__r2), ranges::end(__r2), + std::move(__pred), + std::move(__proj1), std::move(__proj2)); + } + }; + + inline constexpr __mismatch_fn mismatch{}; + + struct __search_fn + { + template<forward_iterator _Iter1, sentinel_for<_Iter1> _Sent1, + forward_iterator _Iter2, sentinel_for<_Iter2> _Sent2, + typename _Pred = ranges::equal_to, + typename _Proj1 = identity, typename _Proj2 = identity> + requires indirectly_comparable<_Iter1, _Iter2, _Pred, _Proj1, _Proj2> + constexpr subrange<_Iter1> + operator()(_Iter1 __first1, _Sent1 __last1, + _Iter2 __first2, _Sent2 __last2, _Pred __pred = {}, + _Proj1 __proj1 = {}, _Proj2 __proj2 = {}) const + { + if (__first1 == __last1 || __first2 == __last2) + return {__first1, __first1}; + + for (;;) + { + for (;;) + { + if (__first1 == __last1) + return {__first1, __first1}; + if (std::__invoke(__pred, + std::__invoke(__proj1, *__first1), + std::__invoke(__proj2, *__first2))) + break; + ++__first1; + } + auto __cur1 = __first1; + auto __cur2 = __first2; + for (;;) + { + if (++__cur2 == __last2) + return {__first1, ++__cur1}; + if (++__cur1 == __last1) + return {__cur1, __cur1}; + if (!(bool)std::__invoke(__pred, + std::__invoke(__proj1, *__cur1), + std::__invoke(__proj2, *__cur2))) + { + ++__first1; + break; + } + } + } + } + + template<forward_range _Range1, forward_range _Range2, + typename _Pred = ranges::equal_to, + typename _Proj1 = identity, typename _Proj2 = identity> + requires indirectly_comparable<iterator_t<_Range1>, iterator_t<_Range2>, + _Pred, _Proj1, _Proj2> + constexpr borrowed_subrange_t<_Range1> + operator()(_Range1&& __r1, _Range2&& __r2, _Pred __pred = {}, + _Proj1 __proj1 = {}, _Proj2 __proj2 = {}) const + { + return (*this)(ranges::begin(__r1), ranges::end(__r1), + ranges::begin(__r2), ranges::end(__r2), + std::move(__pred), + std::move(__proj1), std::move(__proj2)); + } + }; + inline constexpr __search_fn search{}; } // namespace ranges using ranges::get; diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges index f96adf63d10..f93a880ff8a 100644 --- a/libstdc++-v3/include/std/ranges +++ b/libstdc++-v3/include/std/ranges @@ -1144,54 +1144,6 @@ namespace views::__adaptor using all_t = decltype(all(std::declval<_Range>())); } // namespace views - // The following simple algos are transcribed from ranges_algo.h to avoid - // having to include that entire header. - namespace __detail - { - template<typename _Iter, typename _Sent, typename _Tp> - constexpr _Iter - find(_Iter __first, _Sent __last, const _Tp& __value) - { - while (__first != __last - && !(bool)(*__first == __value)) - ++__first; - return __first; - } - - template<typename _Iter, typename _Sent, typename _Pred> - constexpr _Iter - find_if(_Iter __first, _Sent __last, _Pred __pred) - { - while (__first != __last - && !(bool)std::__invoke(__pred, *__first)) - ++__first; - return __first; - } - - template<typename _Iter, typename _Sent, typename _Pred> - constexpr _Iter - find_if_not(_Iter __first, _Sent __last, _Pred __pred) - { - while (__first != __last - && (bool)std::__invoke(__pred, *__first)) - ++__first; - return __first; - } - - template<typename _Iter1, typename _Sent1, typename _Iter2, typename _Sent2> - constexpr pair<_Iter1, _Iter2> - mismatch(_Iter1 __first1, _Sent1 __last1, _Iter2 __first2, _Sent2 __last2) - { - while (__first1 != __last1 && __first2 != __last2 - && (bool)ranges::equal_to{}(*__first1, *__first2)) - { - ++__first1; - ++__first2; - } - return { std::move(__first1), std::move(__first2) }; - } - } // namespace __detail - namespace __detail { template<typename _Tp> @@ -1449,9 +1401,9 @@ namespace views::__adaptor constexpr _Iterator& operator++() { - _M_current = __detail::find_if(std::move(++_M_current), - ranges::end(_M_parent->_M_base), - std::ref(*_M_parent->_M_pred)); + _M_current = ranges::find_if(std::move(++_M_current), + ranges::end(_M_parent->_M_base), + std::ref(*_M_parent->_M_pred)); return *this; } @@ -1560,9 +1512,9 @@ namespace views::__adaptor return {this, _M_cached_begin._M_get(_M_base)}; __glibcxx_assert(_M_pred.has_value()); - auto __it = __detail::find_if(ranges::begin(_M_base), - ranges::end(_M_base), - std::ref(*_M_pred)); + auto __it = ranges::find_if(ranges::begin(_M_base), + ranges::end(_M_base), + std::ref(*_M_pred)); _M_cached_begin._M_set(_M_base, __it); return {this, std::move(__it)}; } @@ -2453,9 +2405,9 @@ namespace views::__adaptor return _M_cached_begin._M_get(_M_base); __glibcxx_assert(_M_pred.has_value()); - auto __it = __detail::find_if_not(ranges::begin(_M_base), - ranges::end(_M_base), - std::cref(*_M_pred)); + auto __it = ranges::find_if_not(ranges::begin(_M_base), + ranges::end(_M_base), + std::cref(*_M_pred)); _M_cached_begin._M_set(_M_base, __it); return __it; } @@ -3031,8 +2983,8 @@ namespace views::__adaptor ++__current(); else if constexpr (__detail::__tiny_range<_Pattern>) { - __current() = __detail::find(std::move(__current()), __end, - *__pbegin); + __current() = ranges::find(std::move(__current()), __end, + *__pbegin); if (__current() != __end) ++__current(); } @@ -3040,7 +2992,7 @@ namespace views::__adaptor do { auto [__b, __p] - = __detail::mismatch(__current(), __end, __pbegin, __pend); + = ranges::mismatch(__current(), __end, __pbegin, __pend); if (__p == __pend) { __current() = __b; -- 2.32.0.93.g670b81a890 ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 2/5] libstdc++: Move ranges algos used by <ranges> into ranges_util.h 2021-06-17 15:22 ` [PATCH 2/5] libstdc++: Move ranges algos used by <ranges> into ranges_util.h Patrick Palka @ 2021-06-17 18:14 ` Jonathan Wakely 0 siblings, 0 replies; 11+ messages in thread From: Jonathan Wakely @ 2021-06-17 18:14 UTC (permalink / raw) To: Patrick Palka; +Cc: gcc-patches, libstdc++ On Thu, 17 Jun 2021, 16:47 Patrick Palka via Libstdc++, < libstdc++@gcc.gnu.org> wrote: > The <ranges> header defines simplified copies of some ranges algorithms > in order to avoid including the entirety of ranges_algo.h. A subsequent > patch is going to want to use ranges::search in <ranges> as well, but > that algorithm is more complicated compared to the other copied ones. > > So rather than additionally copying ranges::search into <ranges>, this > patch splits out all the ranges algos used by <ranges> (including > ranges::search) from ranges_algo.h to ranges_util.h, and deletes the > simplified copies in <ranges>. This seems like the best place for > these algorithms, as ranges_util.h is included only from <ranges> and > ranges_algo.h. > OK, thanks. ^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 3/5] libstdc++: Rename views::split to views::lazy_split as per P2210 2021-06-17 15:22 [PATCH 1/5] libstdc++: Implement P2325 changes to default-constructibilty of views Patrick Palka 2021-06-17 15:22 ` [PATCH 2/5] libstdc++: Move ranges algos used by <ranges> into ranges_util.h Patrick Palka @ 2021-06-17 15:22 ` Patrick Palka 2021-06-18 21:56 ` Jonathan Wakely 2021-06-17 15:22 ` [PATCH 4/5] libstdc++: Implement resolution of LWG 3478 " Patrick Palka ` (2 subsequent siblings) 4 siblings, 1 reply; 11+ messages in thread From: Patrick Palka @ 2021-06-17 15:22 UTC (permalink / raw) To: gcc-patches; +Cc: libstdc++, Patrick Palka This mostly mechanical patch performs the renaming part of P2210R3 "Superior string splitting". It also defines _InnerIter::base() overloads. libstdc++-v3/ChangeLog: * include/std/ranges: Rename views::split to views::lazy_split, split_view to lazy_split_view, etc. throughout. (lazy_split_view::_InnerIter::base): Define as per P2210. * testsuite/std/ranges/*: Likewise. --- libstdc++-v3/include/std/ranges | 68 +++++++++++-------- .../testsuite/std/ranges/adaptors/100479.cc | 2 +- .../testsuite/std/ranges/adaptors/100577.cc | 20 +++--- .../testsuite/std/ranges/adaptors/join.cc | 2 +- .../adaptors/{split.cc => lazy_split.cc} | 54 +++++++-------- .../{split_neg.cc => lazy_split_neg.cc} | 6 +- .../testsuite/std/ranges/adaptors/p2281.cc | 18 ++--- .../testsuite/std/ranges/adaptors/sizeof.cc | 2 +- libstdc++-v3/testsuite/std/ranges/p2259.cc | 6 +- libstdc++-v3/testsuite/std/ranges/p2325.cc | 10 +-- libstdc++-v3/testsuite/std/ranges/p2367.cc | 4 +- 11 files changed, 100 insertions(+), 92 deletions(-) rename libstdc++-v3/testsuite/std/ranges/adaptors/{split.cc => lazy_split.cc} (76%) rename libstdc++-v3/testsuite/std/ranges/adaptors/{split_neg.cc => lazy_split_neg.cc} (79%) diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges index f93a880ff8a..cc1ef112ff1 100644 --- a/libstdc++-v3/include/std/ranges +++ b/libstdc++-v3/include/std/ranges @@ -2826,19 +2826,19 @@ namespace views::__adaptor && (remove_reference_t<_Range>::size() <= 1); template<typename _Base> - struct __split_view_outer_iter_cat + struct __lazy_split_view_outer_iter_cat { }; template<forward_range _Base> - struct __split_view_outer_iter_cat<_Base> + struct __lazy_split_view_outer_iter_cat<_Base> { using iterator_category = input_iterator_tag; }; template<typename _Base> - struct __split_view_inner_iter_cat + struct __lazy_split_view_inner_iter_cat { }; template<forward_range _Base> - struct __split_view_inner_iter_cat<_Base> + struct __lazy_split_view_inner_iter_cat<_Base> { private: static constexpr auto @@ -2860,7 +2860,7 @@ namespace views::__adaptor && indirectly_comparable<iterator_t<_Vp>, iterator_t<_Pattern>, ranges::equal_to> && (forward_range<_Vp> || __detail::__tiny_range<_Pattern>) - class split_view : public view_interface<split_view<_Vp, _Pattern>> + class lazy_split_view : public view_interface<lazy_split_view<_Vp, _Pattern>> { private: template<bool _Const> @@ -2871,17 +2871,17 @@ namespace views::__adaptor template<bool _Const> struct _OuterIter - : __detail::__split_view_outer_iter_cat<_Base<_Const>> + : __detail::__lazy_split_view_outer_iter_cat<_Base<_Const>> { private: - using _Parent = __detail::__maybe_const_t<_Const, split_view>; - using _Base = split_view::_Base<_Const>; + using _Parent = __detail::__maybe_const_t<_Const, lazy_split_view>; + using _Base = lazy_split_view::_Base<_Const>; constexpr bool __at_end() const { return __current() == ranges::end(_M_parent->_M_base); } - // [range.split.outer] p1 + // [range.lazy.split.outer] p1 // Many of the following specifications refer to the notional member // current of outer-iterator. current is equivalent to current_ if // V models forward_range, and parent_->current_ otherwise. @@ -2914,7 +2914,7 @@ namespace views::__adaptor using iterator_concept = conditional_t<forward_range<_Base>, forward_iterator_tag, input_iterator_tag>; - // iterator_category defined in __split_view_outer_iter_cat + // iterator_category defined in __lazy_split_view_outer_iter_cat using difference_type = range_difference_t<_Base>; struct value_type : view_interface<value_type> @@ -2974,7 +2974,7 @@ namespace views::__adaptor operator++() { // _GLIBCXX_RESOLVE_LIB_DEFECTS - // 3505. split_view::outer-iterator::operator++ misspecified + // 3505. lazy_split_view::outer-iterator::operator++ misspecified const auto __end = ranges::end(_M_parent->_M_base); if (__current() == __end) return *this; @@ -3030,10 +3030,10 @@ namespace views::__adaptor template<bool _Const> struct _InnerIter - : __detail::__split_view_inner_iter_cat<_Base<_Const>> + : __detail::__lazy_split_view_inner_iter_cat<_Base<_Const>> { private: - using _Base = split_view::_Base<_Const>; + using _Base = lazy_split_view::_Base<_Const>; constexpr bool __at_end() const @@ -3081,7 +3081,7 @@ namespace views::__adaptor public: using iterator_concept = typename _OuterIter<_Const>::iterator_concept; - // iterator_category defined in __split_view_inner_iter_cat + // iterator_category defined in __lazy_split_view_inner_iter_cat using value_type = range_value_t<_Base>; using difference_type = range_difference_t<_Base>; @@ -3092,6 +3092,14 @@ namespace views::__adaptor : _M_i(std::move(__i)) { } + constexpr iterator_t<_Base> + base() const& requires copyable<iterator_t<_Base>> + { return _M_i_current(); } + + constexpr iterator_t<_Base> + base() && + { return std::move(_M_i_current()); } + constexpr decltype(auto) operator*() const { return *_M_i_current(); } @@ -3151,12 +3159,12 @@ namespace views::__adaptor public: - split_view() requires (default_initializable<_Vp> - && default_initializable<_Pattern>) + lazy_split_view() requires (default_initializable<_Vp> + && default_initializable<_Pattern>) = default; constexpr - split_view(_Vp __base, _Pattern __pattern) + lazy_split_view(_Vp __base, _Pattern __pattern) : _M_pattern(std::move(__pattern)), _M_base(std::move(__base)) { } @@ -3164,7 +3172,7 @@ namespace views::__adaptor requires constructible_from<_Vp, views::all_t<_Range>> && constructible_from<_Pattern, single_view<range_value_t<_Range>>> constexpr - split_view(_Range&& __r, range_value_t<_Range> __e) + lazy_split_view(_Range&& __r, range_value_t<_Range> __e) : _M_pattern(views::single(std::move(__e))), _M_base(views::all(std::forward<_Range>(__r))) { } @@ -3216,35 +3224,35 @@ namespace views::__adaptor }; template<typename _Range, typename _Pattern> - split_view(_Range&&, _Pattern&&) - -> split_view<views::all_t<_Range>, views::all_t<_Pattern>>; + lazy_split_view(_Range&&, _Pattern&&) + -> lazy_split_view<views::all_t<_Range>, views::all_t<_Pattern>>; template<input_range _Range> - split_view(_Range&&, range_value_t<_Range>) - -> split_view<views::all_t<_Range>, single_view<range_value_t<_Range>>>; + lazy_split_view(_Range&&, range_value_t<_Range>) + -> lazy_split_view<views::all_t<_Range>, single_view<range_value_t<_Range>>>; namespace views { namespace __detail { template<typename _Range, typename _Pattern> - concept __can_split_view - = requires { split_view(std::declval<_Range>(), std::declval<_Pattern>()); }; + concept __can_lazy_split_view + = requires { lazy_split_view(std::declval<_Range>(), std::declval<_Pattern>()); }; } // namespace __detail - struct _Split : __adaptor::_RangeAdaptor<_Split> + struct _LazySplit : __adaptor::_RangeAdaptor<_LazySplit> { template<viewable_range _Range, typename _Pattern> - requires __detail::__can_split_view<_Range, _Pattern> + requires __detail::__can_lazy_split_view<_Range, _Pattern> constexpr auto operator()(_Range&& __r, _Pattern&& __f) const { - return split_view(std::forward<_Range>(__r), std::forward<_Pattern>(__f)); + return lazy_split_view(std::forward<_Range>(__r), std::forward<_Pattern>(__f)); } - using _RangeAdaptor<_Split>::operator(); + using _RangeAdaptor<_LazySplit>::operator(); static constexpr int _S_arity = 2; - // The pattern argument of views::split is not always simple -- it can be + // The pattern argument of views::lazy_split is not always simple -- it can be // a non-view range, the value category of which affects whether the call // is well-formed. But a scalar or a view pattern argument is surely // simple. @@ -3254,7 +3262,7 @@ namespace views::__adaptor && copy_constructible<_Pattern>); }; - inline constexpr _Split split; + inline constexpr _LazySplit lazy_split; } // namespace views namespace views diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/100479.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/100479.cc index ba10b7baf3f..9899ff92c0b 100644 --- a/libstdc++-v3/testsuite/std/ranges/adaptors/100479.cc +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/100479.cc @@ -90,7 +90,7 @@ test03() // Propagating cached iterators during copy/move would cause these asserts // to fail here. auto v = views::single(1) - | views::split(1) + | views::lazy_split(1) | views::drop(0) | views::drop_while([](auto) { return false; }) | views::filter([](auto) { return true; }); diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/100577.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/100577.cc index 06be4980ddb..5ef7f3f59a7 100644 --- a/libstdc++-v3/testsuite/std/ranges/adaptors/100577.cc +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/100577.cc @@ -38,11 +38,11 @@ test01() static_assert(__adaptor_has_simple_extra_args<decltype(views::take), int>); static_assert(__adaptor_has_simple_extra_args<decltype(views::take_while), identity>); static_assert(__adaptor_has_simple_extra_args<decltype(views::drop_while), identity>); - static_assert(__adaptor_has_simple_extra_args<decltype(views::split), std::string_view>); - static_assert(__adaptor_has_simple_extra_args<decltype(views::split), char>); - static_assert(!__adaptor_has_simple_extra_args<decltype(views::split), std::string>); + static_assert(__adaptor_has_simple_extra_args<decltype(views::lazy_split), std::string_view>); + static_assert(__adaptor_has_simple_extra_args<decltype(views::lazy_split), char>); + static_assert(!__adaptor_has_simple_extra_args<decltype(views::lazy_split), std::string>); - // Verify all adaptor closures except for views::split(pattern) have a simple + // Verify all adaptor closures except for views::lazy_split(pattern) have a simple // operator(). using views::__adaptor::__closure_has_simple_call_op; __closure_has_simple_call_op auto a00 = views::all; @@ -56,14 +56,14 @@ test01() __closure_has_simple_call_op auto a08 = views::common; __closure_has_simple_call_op auto a09 = views::reverse; __closure_has_simple_call_op auto a10 = views::keys; - __closure_has_simple_call_op auto a11 = views::split(' '); + __closure_has_simple_call_op auto a11 = views::lazy_split(' '); // Verify composition of simple closures is simple. __closure_has_simple_call_op auto b = (a00 | a01) | (a02 | a03) | (a04 | a05 | a06) | (a07 | a08 | a09 | a10) | a11; - // Verify views::split(non_view_range) is an exception. + // Verify views::lazy_split(non_view_range) is an exception. extern std::string s; - auto a12 = views::split(s); + auto a12 = views::lazy_split(s); static_assert(!__closure_has_simple_call_op<decltype(a12)>); static_assert(!__closure_has_simple_call_op<decltype(a12 | a00)>); static_assert(!__closure_has_simple_call_op<decltype(a00 | a12)>); @@ -91,9 +91,9 @@ test02() // implemented using a fallback deleted overload, so when a call is // ill-formed overload resolution succeeds but selects the deleted overload // (but only when the closure is invoked as an rvalue). - views::split(badarg)(x); // { dg-error "deleted function" } - (views::split(badarg) | views::all)(x); // { dg-error "deleted function" } - auto a0 = views::split(badarg); + views::lazy_split(badarg)(x); // { dg-error "deleted function" } + (views::lazy_split(badarg) | views::all)(x); // { dg-error "deleted function" } + auto a0 = views::lazy_split(badarg); a0(x); // { dg-error "no match" }; auto a1 = a0 | views::all; a1(x); // { dg-error "no match" } diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc index d774e8d9385..50af3fdf729 100644 --- a/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc @@ -93,7 +93,7 @@ test05() { using namespace std::literals; std::vector<std::string> x = {"the", " ", "quick", " ", "brown", " ", "fox"}; - auto v = x | views::join | views::split(' '); + auto v = x | views::join | views::lazy_split(' '); auto i = v.begin(); VERIFY( ranges::equal(*i++, "the"sv) ); VERIFY( ranges::equal(*i++, "quick"sv) ); diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/split.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split.cc similarity index 76% rename from libstdc++-v3/testsuite/std/ranges/adaptors/split.cc rename to libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split.cc index 9d2cfa8632a..12844525d86 100644 --- a/libstdc++-v3/testsuite/std/ranges/adaptors/split.cc +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split.cc @@ -39,7 +39,7 @@ test01() { auto x = "the quick brown fox"sv; auto p = std::string{" "}; - auto v = x | views::split(views::all(p)); // views::all is needed here after P2281. + auto v = x | views::lazy_split(views::all(p)); // views::all is needed here after P2281. auto i = v.begin(); VERIFY( ranges::equal(*i++, "the"sv) ); VERIFY( ranges::equal(*i++, "quick"sv) ); @@ -52,7 +52,7 @@ void test02() { auto x = "the quick brown fox"sv; - auto v = x | views::split(' '); + auto v = x | views::lazy_split(' '); auto i = v.begin(); VERIFY( ranges::equal(*i++, "the"sv) ); VERIFY( ranges::equal(*i++, "quick"sv) ); @@ -66,7 +66,7 @@ test03() { char x[] = "the quick brown fox"; test_range<char, forward_iterator_wrapper> rx(x, x+sizeof(x)-1); - auto v = rx | views::split(' '); + auto v = rx | views::lazy_split(' '); auto i = v.begin(); VERIFY( ranges::equal(*i++, "the"sv) ); VERIFY( ranges::equal(*i++, "quick"sv) ); @@ -83,7 +83,7 @@ test04() static_assert(!ranges::view<decltype(p)>); static_assert(std::same_as<decltype(p | views::all), ranges::ref_view<decltype(p)>>); - auto v = x | views::split(views::all(p)); // views::all is needed here after P2281. + auto v = x | views::lazy_split(views::all(p)); // views::all is needed here after P2281. auto i = v.begin(); VERIFY( ranges::equal(*i++, "the"sv) ); VERIFY( ranges::equal(*i++, "quick"sv) ); @@ -102,7 +102,7 @@ test05() std::string str = "Now is the time for all good men to come to the aid of their county."; auto rng - = str | views::split(' ') | views::transform(as_string) | views::common; + = str | views::lazy_split(' ') | views::transform(as_string) | views::common; std::vector<std::string> words(rng.begin(), rng.end()); auto not_space_p = [](char c) { return c != ' '; }; VERIFY( ranges::equal(words | views::join, @@ -113,7 +113,7 @@ void test06() { std::string str = "hello world"; - auto v = str | views::transform(std::identity{}) | views::split(' '); + auto v = str | views::transform(std::identity{}) | views::lazy_split(' '); // Verify that _Iterator<false> is implicitly convertible to _Iterator<true>. static_assert(!std::same_as<decltype(ranges::begin(v)), @@ -126,7 +126,7 @@ void test07() { char str[] = "banana split"; - auto split = str | views::split(' '); + auto split = str | views::lazy_split(' '); auto val = *split.begin(); auto b = val.begin(); auto b2 = b++; @@ -139,7 +139,7 @@ test08() { char x[] = "the quick brown fox"; test_range<char, input_iterator_wrapper> rx(x, x+sizeof(x)-1); - auto v = rx | views::split(' '); + auto v = rx | views::lazy_split(' '); auto i = v.begin(); VERIFY( ranges::equal(*i, "the"sv) ); ++i; @@ -152,32 +152,32 @@ test08() VERIFY( i == v.end() ); } -template<auto split = views::split> +template<auto lazy_split = views::lazy_split> void test09() { // Verify SFINAE behavior. std::string s, p; - static_assert(!requires { split(); }); - static_assert(!requires { split(s, p, 0); }); - static_assert(!requires { split(p)(); }); - static_assert(!requires { s | split; }); - - static_assert(!requires { s | split(p); }); - static_assert(!requires { split(p)(s); }); - static_assert(!requires { s | (split(p) | views::all); }); - static_assert(!requires { (split(p) | views::all)(s); }); - - static_assert(requires { s | split(views::all(p)); }); - static_assert(requires { split(views::all(p))(s); }); - static_assert(requires { s | (split(views::all(p)) | views::all); }); - static_assert(requires { (split(views::all(p)) | views::all)(s); }); - - auto adapt = split(p); + static_assert(!requires { lazy_split(); }); + static_assert(!requires { lazy_split(s, p, 0); }); + static_assert(!requires { lazy_split(p)(); }); + static_assert(!requires { s | lazy_split; }); + + static_assert(!requires { s | lazy_split(p); }); + static_assert(!requires { lazy_split(p)(s); }); + static_assert(!requires { s | (lazy_split(p) | views::all); }); + static_assert(!requires { (lazy_split(p) | views::all)(s); }); + + static_assert(requires { s | lazy_split(views::all(p)); }); + static_assert(requires { lazy_split(views::all(p))(s); }); + static_assert(requires { s | (lazy_split(views::all(p)) | views::all); }); + static_assert(requires { (lazy_split(views::all(p)) | views::all)(s); }); + + auto adapt = lazy_split(p); static_assert(requires { s | adapt; }); static_assert(requires { adapt(s); }); - auto adapt2 = split(p) | views::all; + auto adapt2 = lazy_split(p) | views::all; static_assert(requires { s | adapt2; }); static_assert(requires { adapt2(s); }); } @@ -189,7 +189,7 @@ test10() auto to_string = [] (auto r) { return std::string(r.begin(), ranges::next(r.begin(), r.end())); }; - auto v = "xxyx"sv | views::split("xy"sv) | views::transform(to_string); + auto v = "xxyx"sv | views::lazy_split("xy"sv) | views::transform(to_string); VERIFY( ranges::equal(v, (std::string_view[]){"x", "x"}) ); } diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/split_neg.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split_neg.cc similarity index 79% rename from libstdc++-v3/testsuite/std/ranges/adaptors/split_neg.cc rename to libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split_neg.cc index 4229314a9dc..c59f828ee56 100644 --- a/libstdc++-v3/testsuite/std/ranges/adaptors/split_neg.cc +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split_neg.cc @@ -30,7 +30,7 @@ test01() { using namespace std::literals; auto x = "the quick brown fox"sv; - auto v = views::split(x, std::initializer_list<char>{' ', ' '}); // { dg-error "no match" } + auto v = views::lazy_split(x, std::initializer_list<char>{' ', ' '}); // { dg-error "no match" } } void @@ -38,8 +38,8 @@ test02() { using namespace std::literals; auto x = "the quick brown fox"sv; - auto v1 = views::split(std::initializer_list<char>{' ', ' '})(x); // { dg-error "deleted" } - auto v2 = x | views::split(std::initializer_list<char>{' ', ' '}); // { dg-error "no match" } + auto v1 = views::lazy_split(std::initializer_list<char>{' ', ' '})(x); // { dg-error "deleted" } + auto v2 = x | views::lazy_split(std::initializer_list<char>{' ', ' '}); // { dg-error "no match" } } // { dg-prune-output "in requirements" } diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/p2281.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/p2281.cc index c916a5ea8b7..7950c43576d 100644 --- a/libstdc++-v3/testsuite/std/ranges/adaptors/p2281.cc +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/p2281.cc @@ -34,7 +34,7 @@ void test01() { auto split_into_strings = [] (auto p) { - return views::split(p) | views::transform([](auto r){ + return views::lazy_split(p) | views::transform([](auto r){ return std::string(r.begin(), ranges::next(r.begin(), r.end())); }); }; @@ -60,19 +60,19 @@ struct move_only_range template<> inline constexpr bool std::ranges::enable_view<move_only_range> = true; -template<auto split = views::split> +template<auto lazy_split = views::lazy_split> void test02() { std::string_view s; move_only_range p; - static_assert(requires { s | split(std::move(p)); }); - static_assert(requires { split(std::move(p))(s); }); - static_assert(requires { split(std::move(p)) | views::all; }); - static_assert(requires { views::all | split(std::move(p)); }); - static_assert(!requires { split(p); }); - static_assert(!requires { split(p) | views::all; }); - static_assert(!requires { views::all | split(p); }); + static_assert(requires { s | lazy_split(std::move(p)); }); + static_assert(requires { lazy_split(std::move(p))(s); }); + static_assert(requires { lazy_split(std::move(p)) | views::all; }); + static_assert(requires { views::all | lazy_split(std::move(p)); }); + static_assert(!requires { lazy_split(p); }); + static_assert(!requires { lazy_split(p) | views::all; }); + static_assert(!requires { views::all | lazy_split(p); }); } int diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/sizeof.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/sizeof.cc index 80326f8bf21..219e2a61f07 100644 --- a/libstdc++-v3/testsuite/std/ranges/adaptors/sizeof.cc +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/sizeof.cc @@ -46,7 +46,7 @@ static_assert(sizeof(ranges::take_while_view<V, decltype(pred_l)>) == 3*ptr); static_assert(sizeof(ranges::drop_while_view<V, decltype(pred_l)>) == 3*ptr); static_assert(sizeof(ranges::transform_view<V, decltype(func_l)>) == 3*ptr); -static_assert(sizeof(ranges::split_view<V, std::string_view>) == 4*ptr); +static_assert(sizeof(ranges::lazy_split_view<V, std::string_view>) == 4*ptr); static_assert (sizeof(ranges::reverse_view<ranges::filter_view<V, decltype(pred_l)>>) == 4*ptr); diff --git a/libstdc++-v3/testsuite/std/ranges/p2259.cc b/libstdc++-v3/testsuite/std/ranges/p2259.cc index 1b422e44f16..0ec7e21f657 100644 --- a/libstdc++-v3/testsuite/std/ranges/p2259.cc +++ b/libstdc++-v3/testsuite/std/ranges/p2259.cc @@ -49,12 +49,12 @@ test01() // Verify the changes to transform_view. only_cxx20_input_range auto v2 = v0 | views::transform([](int& c) -> auto& { return c; }); - // Verify the changes to split_view. - only_cxx20_input_range auto v3 = v0 | views::split(12); + // Verify the changes to lazy_split_view. + only_cxx20_input_range auto v3 = v0 | views::lazy_split(12); static_assert(only_cxx20_input_range<decltype(*v3.begin())>); // Verify the changes to join_view. - only_cxx20_input_range auto v4 = v0 | views::split(12) | views::join; + only_cxx20_input_range auto v4 = v0 | views::lazy_split(12) | views::join; // Verify the changes to elements_view. only_cxx20_input_range auto v5 diff --git a/libstdc++-v3/testsuite/std/ranges/p2325.cc b/libstdc++-v3/testsuite/std/ranges/p2325.cc index df6cde29e4d..4d075409026 100644 --- a/libstdc++-v3/testsuite/std/ranges/p2325.cc +++ b/libstdc++-v3/testsuite/std/ranges/p2325.cc @@ -113,14 +113,14 @@ test07() void test08() { - // Verify split_view is conditionally default constructible. - using type1 = ranges::split_view<ranges::ref_view<int[2]>, ranges::single_view<int>>; + // Verify lazy_split_view is conditionally default constructible. + using type1 = ranges::lazy_split_view<ranges::ref_view<int[2]>, ranges::single_view<int>>; static_assert(!default_initializable<type1>); - using type2 = ranges::split_view<ranges::single_view<int>, ranges::ref_view<int[2]>>; + using type2 = ranges::lazy_split_view<ranges::single_view<int>, ranges::ref_view<int[2]>>; static_assert(!default_initializable<type2>); - using type3 = ranges::split_view<ranges::ref_view<int[2]>, ranges::ref_view<int[2]>>; + using type3 = ranges::lazy_split_view<ranges::ref_view<int[2]>, ranges::ref_view<int[2]>>; static_assert(!default_initializable<type3>); - using type4 = ranges::split_view<ranges::single_view<int>, ranges::single_view<int>>; + using type4 = ranges::lazy_split_view<ranges::single_view<int>, ranges::single_view<int>>; static_assert(default_initializable<type4>); } diff --git a/libstdc++-v3/testsuite/std/ranges/p2367.cc b/libstdc++-v3/testsuite/std/ranges/p2367.cc index 40875233d88..5228b021602 100644 --- a/libstdc++-v3/testsuite/std/ranges/p2367.cc +++ b/libstdc++-v3/testsuite/std/ranges/p2367.cc @@ -43,6 +43,6 @@ test01() // Verify changes to views::drop. auto v5 = views::drop(x, 0ull); - // Verify changes to views::split. - auto v6 = views::split(x, 5u); + // Verify changes to views::lazy_split. + auto v6 = views::lazy_split(x, 5u); } -- 2.32.0.93.g670b81a890 ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 3/5] libstdc++: Rename views::split to views::lazy_split as per P2210 2021-06-17 15:22 ` [PATCH 3/5] libstdc++: Rename views::split to views::lazy_split as per P2210 Patrick Palka @ 2021-06-18 21:56 ` Jonathan Wakely 0 siblings, 0 replies; 11+ messages in thread From: Jonathan Wakely @ 2021-06-18 21:56 UTC (permalink / raw) To: Patrick Palka; +Cc: gcc Patches, libstdc++ On Thu, 17 Jun 2021 at 16:55, Patrick Palka via Libstdc++ <libstdc++@gcc.gnu.org> wrote: > > This mostly mechanical patch performs the renaming part of P2210R3 > "Superior string splitting". It also defines _InnerIter::base() > overloads. > > libstdc++-v3/ChangeLog: > > * include/std/ranges: Rename views::split to views::lazy_split, > split_view to lazy_split_view, etc. throughout. > (lazy_split_view::_InnerIter::base): Define as per P2210. > * testsuite/std/ranges/*: Likewise. The base() member could be noexcept, but that can be added later. OK. > --- > libstdc++-v3/include/std/ranges | 68 +++++++++++-------- > .../testsuite/std/ranges/adaptors/100479.cc | 2 +- > .../testsuite/std/ranges/adaptors/100577.cc | 20 +++--- > .../testsuite/std/ranges/adaptors/join.cc | 2 +- > .../adaptors/{split.cc => lazy_split.cc} | 54 +++++++-------- > .../{split_neg.cc => lazy_split_neg.cc} | 6 +- > .../testsuite/std/ranges/adaptors/p2281.cc | 18 ++--- > .../testsuite/std/ranges/adaptors/sizeof.cc | 2 +- > libstdc++-v3/testsuite/std/ranges/p2259.cc | 6 +- > libstdc++-v3/testsuite/std/ranges/p2325.cc | 10 +-- > libstdc++-v3/testsuite/std/ranges/p2367.cc | 4 +- > 11 files changed, 100 insertions(+), 92 deletions(-) > rename libstdc++-v3/testsuite/std/ranges/adaptors/{split.cc => lazy_split.cc} (76%) > rename libstdc++-v3/testsuite/std/ranges/adaptors/{split_neg.cc => lazy_split_neg.cc} (79%) > > diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges > index f93a880ff8a..cc1ef112ff1 100644 > --- a/libstdc++-v3/include/std/ranges > +++ b/libstdc++-v3/include/std/ranges > @@ -2826,19 +2826,19 @@ namespace views::__adaptor > && (remove_reference_t<_Range>::size() <= 1); > > template<typename _Base> > - struct __split_view_outer_iter_cat > + struct __lazy_split_view_outer_iter_cat > { }; > > template<forward_range _Base> > - struct __split_view_outer_iter_cat<_Base> > + struct __lazy_split_view_outer_iter_cat<_Base> > { using iterator_category = input_iterator_tag; }; > > template<typename _Base> > - struct __split_view_inner_iter_cat > + struct __lazy_split_view_inner_iter_cat > { }; > > template<forward_range _Base> > - struct __split_view_inner_iter_cat<_Base> > + struct __lazy_split_view_inner_iter_cat<_Base> > { > private: > static constexpr auto > @@ -2860,7 +2860,7 @@ namespace views::__adaptor > && indirectly_comparable<iterator_t<_Vp>, iterator_t<_Pattern>, > ranges::equal_to> > && (forward_range<_Vp> || __detail::__tiny_range<_Pattern>) > - class split_view : public view_interface<split_view<_Vp, _Pattern>> > + class lazy_split_view : public view_interface<lazy_split_view<_Vp, _Pattern>> > { > private: > template<bool _Const> > @@ -2871,17 +2871,17 @@ namespace views::__adaptor > > template<bool _Const> > struct _OuterIter > - : __detail::__split_view_outer_iter_cat<_Base<_Const>> > + : __detail::__lazy_split_view_outer_iter_cat<_Base<_Const>> > { > private: > - using _Parent = __detail::__maybe_const_t<_Const, split_view>; > - using _Base = split_view::_Base<_Const>; > + using _Parent = __detail::__maybe_const_t<_Const, lazy_split_view>; > + using _Base = lazy_split_view::_Base<_Const>; > > constexpr bool > __at_end() const > { return __current() == ranges::end(_M_parent->_M_base); } > > - // [range.split.outer] p1 > + // [range.lazy.split.outer] p1 > // Many of the following specifications refer to the notional member > // current of outer-iterator. current is equivalent to current_ if > // V models forward_range, and parent_->current_ otherwise. > @@ -2914,7 +2914,7 @@ namespace views::__adaptor > using iterator_concept = conditional_t<forward_range<_Base>, > forward_iterator_tag, > input_iterator_tag>; > - // iterator_category defined in __split_view_outer_iter_cat > + // iterator_category defined in __lazy_split_view_outer_iter_cat > using difference_type = range_difference_t<_Base>; > > struct value_type : view_interface<value_type> > @@ -2974,7 +2974,7 @@ namespace views::__adaptor > operator++() > { > // _GLIBCXX_RESOLVE_LIB_DEFECTS > - // 3505. split_view::outer-iterator::operator++ misspecified > + // 3505. lazy_split_view::outer-iterator::operator++ misspecified > const auto __end = ranges::end(_M_parent->_M_base); > if (__current() == __end) > return *this; > @@ -3030,10 +3030,10 @@ namespace views::__adaptor > > template<bool _Const> > struct _InnerIter > - : __detail::__split_view_inner_iter_cat<_Base<_Const>> > + : __detail::__lazy_split_view_inner_iter_cat<_Base<_Const>> > { > private: > - using _Base = split_view::_Base<_Const>; > + using _Base = lazy_split_view::_Base<_Const>; > > constexpr bool > __at_end() const > @@ -3081,7 +3081,7 @@ namespace views::__adaptor > public: > using iterator_concept > = typename _OuterIter<_Const>::iterator_concept; > - // iterator_category defined in __split_view_inner_iter_cat > + // iterator_category defined in __lazy_split_view_inner_iter_cat > using value_type = range_value_t<_Base>; > using difference_type = range_difference_t<_Base>; > > @@ -3092,6 +3092,14 @@ namespace views::__adaptor > : _M_i(std::move(__i)) > { } > > + constexpr iterator_t<_Base> > + base() const& requires copyable<iterator_t<_Base>> > + { return _M_i_current(); } > + > + constexpr iterator_t<_Base> > + base() && > + { return std::move(_M_i_current()); } > + > constexpr decltype(auto) > operator*() const > { return *_M_i_current(); } > @@ -3151,12 +3159,12 @@ namespace views::__adaptor > > > public: > - split_view() requires (default_initializable<_Vp> > - && default_initializable<_Pattern>) > + lazy_split_view() requires (default_initializable<_Vp> > + && default_initializable<_Pattern>) > = default; > > constexpr > - split_view(_Vp __base, _Pattern __pattern) > + lazy_split_view(_Vp __base, _Pattern __pattern) > : _M_pattern(std::move(__pattern)), _M_base(std::move(__base)) > { } > > @@ -3164,7 +3172,7 @@ namespace views::__adaptor > requires constructible_from<_Vp, views::all_t<_Range>> > && constructible_from<_Pattern, single_view<range_value_t<_Range>>> > constexpr > - split_view(_Range&& __r, range_value_t<_Range> __e) > + lazy_split_view(_Range&& __r, range_value_t<_Range> __e) > : _M_pattern(views::single(std::move(__e))), > _M_base(views::all(std::forward<_Range>(__r))) > { } > @@ -3216,35 +3224,35 @@ namespace views::__adaptor > }; > > template<typename _Range, typename _Pattern> > - split_view(_Range&&, _Pattern&&) > - -> split_view<views::all_t<_Range>, views::all_t<_Pattern>>; > + lazy_split_view(_Range&&, _Pattern&&) > + -> lazy_split_view<views::all_t<_Range>, views::all_t<_Pattern>>; > > template<input_range _Range> > - split_view(_Range&&, range_value_t<_Range>) > - -> split_view<views::all_t<_Range>, single_view<range_value_t<_Range>>>; > + lazy_split_view(_Range&&, range_value_t<_Range>) > + -> lazy_split_view<views::all_t<_Range>, single_view<range_value_t<_Range>>>; > > namespace views > { > namespace __detail > { > template<typename _Range, typename _Pattern> > - concept __can_split_view > - = requires { split_view(std::declval<_Range>(), std::declval<_Pattern>()); }; > + concept __can_lazy_split_view > + = requires { lazy_split_view(std::declval<_Range>(), std::declval<_Pattern>()); }; > } // namespace __detail > > - struct _Split : __adaptor::_RangeAdaptor<_Split> > + struct _LazySplit : __adaptor::_RangeAdaptor<_LazySplit> > { > template<viewable_range _Range, typename _Pattern> > - requires __detail::__can_split_view<_Range, _Pattern> > + requires __detail::__can_lazy_split_view<_Range, _Pattern> > constexpr auto > operator()(_Range&& __r, _Pattern&& __f) const > { > - return split_view(std::forward<_Range>(__r), std::forward<_Pattern>(__f)); > + return lazy_split_view(std::forward<_Range>(__r), std::forward<_Pattern>(__f)); > } > > - using _RangeAdaptor<_Split>::operator(); > + using _RangeAdaptor<_LazySplit>::operator(); > static constexpr int _S_arity = 2; > - // The pattern argument of views::split is not always simple -- it can be > + // The pattern argument of views::lazy_split is not always simple -- it can be > // a non-view range, the value category of which affects whether the call > // is well-formed. But a scalar or a view pattern argument is surely > // simple. > @@ -3254,7 +3262,7 @@ namespace views::__adaptor > && copy_constructible<_Pattern>); > }; > > - inline constexpr _Split split; > + inline constexpr _LazySplit lazy_split; > } // namespace views > > namespace views > diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/100479.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/100479.cc > index ba10b7baf3f..9899ff92c0b 100644 > --- a/libstdc++-v3/testsuite/std/ranges/adaptors/100479.cc > +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/100479.cc > @@ -90,7 +90,7 @@ test03() > // Propagating cached iterators during copy/move would cause these asserts > // to fail here. > auto v = views::single(1) > - | views::split(1) > + | views::lazy_split(1) > | views::drop(0) > | views::drop_while([](auto) { return false; }) > | views::filter([](auto) { return true; }); > diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/100577.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/100577.cc > index 06be4980ddb..5ef7f3f59a7 100644 > --- a/libstdc++-v3/testsuite/std/ranges/adaptors/100577.cc > +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/100577.cc > @@ -38,11 +38,11 @@ test01() > static_assert(__adaptor_has_simple_extra_args<decltype(views::take), int>); > static_assert(__adaptor_has_simple_extra_args<decltype(views::take_while), identity>); > static_assert(__adaptor_has_simple_extra_args<decltype(views::drop_while), identity>); > - static_assert(__adaptor_has_simple_extra_args<decltype(views::split), std::string_view>); > - static_assert(__adaptor_has_simple_extra_args<decltype(views::split), char>); > - static_assert(!__adaptor_has_simple_extra_args<decltype(views::split), std::string>); > + static_assert(__adaptor_has_simple_extra_args<decltype(views::lazy_split), std::string_view>); > + static_assert(__adaptor_has_simple_extra_args<decltype(views::lazy_split), char>); > + static_assert(!__adaptor_has_simple_extra_args<decltype(views::lazy_split), std::string>); > > - // Verify all adaptor closures except for views::split(pattern) have a simple > + // Verify all adaptor closures except for views::lazy_split(pattern) have a simple > // operator(). > using views::__adaptor::__closure_has_simple_call_op; > __closure_has_simple_call_op auto a00 = views::all; > @@ -56,14 +56,14 @@ test01() > __closure_has_simple_call_op auto a08 = views::common; > __closure_has_simple_call_op auto a09 = views::reverse; > __closure_has_simple_call_op auto a10 = views::keys; > - __closure_has_simple_call_op auto a11 = views::split(' '); > + __closure_has_simple_call_op auto a11 = views::lazy_split(' '); > // Verify composition of simple closures is simple. > __closure_has_simple_call_op auto b > = (a00 | a01) | (a02 | a03) | (a04 | a05 | a06) | (a07 | a08 | a09 | a10) | a11; > > - // Verify views::split(non_view_range) is an exception. > + // Verify views::lazy_split(non_view_range) is an exception. > extern std::string s; > - auto a12 = views::split(s); > + auto a12 = views::lazy_split(s); > static_assert(!__closure_has_simple_call_op<decltype(a12)>); > static_assert(!__closure_has_simple_call_op<decltype(a12 | a00)>); > static_assert(!__closure_has_simple_call_op<decltype(a00 | a12)>); > @@ -91,9 +91,9 @@ test02() > // implemented using a fallback deleted overload, so when a call is > // ill-formed overload resolution succeeds but selects the deleted overload > // (but only when the closure is invoked as an rvalue). > - views::split(badarg)(x); // { dg-error "deleted function" } > - (views::split(badarg) | views::all)(x); // { dg-error "deleted function" } > - auto a0 = views::split(badarg); > + views::lazy_split(badarg)(x); // { dg-error "deleted function" } > + (views::lazy_split(badarg) | views::all)(x); // { dg-error "deleted function" } > + auto a0 = views::lazy_split(badarg); > a0(x); // { dg-error "no match" }; > auto a1 = a0 | views::all; > a1(x); // { dg-error "no match" } > diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc > index d774e8d9385..50af3fdf729 100644 > --- a/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc > +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc > @@ -93,7 +93,7 @@ test05() > { > using namespace std::literals; > std::vector<std::string> x = {"the", " ", "quick", " ", "brown", " ", "fox"}; > - auto v = x | views::join | views::split(' '); > + auto v = x | views::join | views::lazy_split(' '); > auto i = v.begin(); > VERIFY( ranges::equal(*i++, "the"sv) ); > VERIFY( ranges::equal(*i++, "quick"sv) ); > diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/split.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split.cc > similarity index 76% > rename from libstdc++-v3/testsuite/std/ranges/adaptors/split.cc > rename to libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split.cc > index 9d2cfa8632a..12844525d86 100644 > --- a/libstdc++-v3/testsuite/std/ranges/adaptors/split.cc > +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split.cc > @@ -39,7 +39,7 @@ test01() > { > auto x = "the quick brown fox"sv; > auto p = std::string{" "}; > - auto v = x | views::split(views::all(p)); // views::all is needed here after P2281. > + auto v = x | views::lazy_split(views::all(p)); // views::all is needed here after P2281. > auto i = v.begin(); > VERIFY( ranges::equal(*i++, "the"sv) ); > VERIFY( ranges::equal(*i++, "quick"sv) ); > @@ -52,7 +52,7 @@ void > test02() > { > auto x = "the quick brown fox"sv; > - auto v = x | views::split(' '); > + auto v = x | views::lazy_split(' '); > auto i = v.begin(); > VERIFY( ranges::equal(*i++, "the"sv) ); > VERIFY( ranges::equal(*i++, "quick"sv) ); > @@ -66,7 +66,7 @@ test03() > { > char x[] = "the quick brown fox"; > test_range<char, forward_iterator_wrapper> rx(x, x+sizeof(x)-1); > - auto v = rx | views::split(' '); > + auto v = rx | views::lazy_split(' '); > auto i = v.begin(); > VERIFY( ranges::equal(*i++, "the"sv) ); > VERIFY( ranges::equal(*i++, "quick"sv) ); > @@ -83,7 +83,7 @@ test04() > static_assert(!ranges::view<decltype(p)>); > static_assert(std::same_as<decltype(p | views::all), > ranges::ref_view<decltype(p)>>); > - auto v = x | views::split(views::all(p)); // views::all is needed here after P2281. > + auto v = x | views::lazy_split(views::all(p)); // views::all is needed here after P2281. > auto i = v.begin(); > VERIFY( ranges::equal(*i++, "the"sv) ); > VERIFY( ranges::equal(*i++, "quick"sv) ); > @@ -102,7 +102,7 @@ test05() > std::string str > = "Now is the time for all good men to come to the aid of their county."; > auto rng > - = str | views::split(' ') | views::transform(as_string) | views::common; > + = str | views::lazy_split(' ') | views::transform(as_string) | views::common; > std::vector<std::string> words(rng.begin(), rng.end()); > auto not_space_p = [](char c) { return c != ' '; }; > VERIFY( ranges::equal(words | views::join, > @@ -113,7 +113,7 @@ void > test06() > { > std::string str = "hello world"; > - auto v = str | views::transform(std::identity{}) | views::split(' '); > + auto v = str | views::transform(std::identity{}) | views::lazy_split(' '); > > // Verify that _Iterator<false> is implicitly convertible to _Iterator<true>. > static_assert(!std::same_as<decltype(ranges::begin(v)), > @@ -126,7 +126,7 @@ void > test07() > { > char str[] = "banana split"; > - auto split = str | views::split(' '); > + auto split = str | views::lazy_split(' '); > auto val = *split.begin(); > auto b = val.begin(); > auto b2 = b++; > @@ -139,7 +139,7 @@ test08() > { > char x[] = "the quick brown fox"; > test_range<char, input_iterator_wrapper> rx(x, x+sizeof(x)-1); > - auto v = rx | views::split(' '); > + auto v = rx | views::lazy_split(' '); > auto i = v.begin(); > VERIFY( ranges::equal(*i, "the"sv) ); > ++i; > @@ -152,32 +152,32 @@ test08() > VERIFY( i == v.end() ); > } > > -template<auto split = views::split> > +template<auto lazy_split = views::lazy_split> > void > test09() > { > // Verify SFINAE behavior. > std::string s, p; > - static_assert(!requires { split(); }); > - static_assert(!requires { split(s, p, 0); }); > - static_assert(!requires { split(p)(); }); > - static_assert(!requires { s | split; }); > - > - static_assert(!requires { s | split(p); }); > - static_assert(!requires { split(p)(s); }); > - static_assert(!requires { s | (split(p) | views::all); }); > - static_assert(!requires { (split(p) | views::all)(s); }); > - > - static_assert(requires { s | split(views::all(p)); }); > - static_assert(requires { split(views::all(p))(s); }); > - static_assert(requires { s | (split(views::all(p)) | views::all); }); > - static_assert(requires { (split(views::all(p)) | views::all)(s); }); > - > - auto adapt = split(p); > + static_assert(!requires { lazy_split(); }); > + static_assert(!requires { lazy_split(s, p, 0); }); > + static_assert(!requires { lazy_split(p)(); }); > + static_assert(!requires { s | lazy_split; }); > + > + static_assert(!requires { s | lazy_split(p); }); > + static_assert(!requires { lazy_split(p)(s); }); > + static_assert(!requires { s | (lazy_split(p) | views::all); }); > + static_assert(!requires { (lazy_split(p) | views::all)(s); }); > + > + static_assert(requires { s | lazy_split(views::all(p)); }); > + static_assert(requires { lazy_split(views::all(p))(s); }); > + static_assert(requires { s | (lazy_split(views::all(p)) | views::all); }); > + static_assert(requires { (lazy_split(views::all(p)) | views::all)(s); }); > + > + auto adapt = lazy_split(p); > static_assert(requires { s | adapt; }); > static_assert(requires { adapt(s); }); > > - auto adapt2 = split(p) | views::all; > + auto adapt2 = lazy_split(p) | views::all; > static_assert(requires { s | adapt2; }); > static_assert(requires { adapt2(s); }); > } > @@ -189,7 +189,7 @@ test10() > auto to_string = [] (auto r) { > return std::string(r.begin(), ranges::next(r.begin(), r.end())); > }; > - auto v = "xxyx"sv | views::split("xy"sv) | views::transform(to_string); > + auto v = "xxyx"sv | views::lazy_split("xy"sv) | views::transform(to_string); > VERIFY( ranges::equal(v, (std::string_view[]){"x", "x"}) ); > } > > diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/split_neg.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split_neg.cc > similarity index 79% > rename from libstdc++-v3/testsuite/std/ranges/adaptors/split_neg.cc > rename to libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split_neg.cc > index 4229314a9dc..c59f828ee56 100644 > --- a/libstdc++-v3/testsuite/std/ranges/adaptors/split_neg.cc > +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split_neg.cc > @@ -30,7 +30,7 @@ test01() > { > using namespace std::literals; > auto x = "the quick brown fox"sv; > - auto v = views::split(x, std::initializer_list<char>{' ', ' '}); // { dg-error "no match" } > + auto v = views::lazy_split(x, std::initializer_list<char>{' ', ' '}); // { dg-error "no match" } > } > > void > @@ -38,8 +38,8 @@ test02() > { > using namespace std::literals; > auto x = "the quick brown fox"sv; > - auto v1 = views::split(std::initializer_list<char>{' ', ' '})(x); // { dg-error "deleted" } > - auto v2 = x | views::split(std::initializer_list<char>{' ', ' '}); // { dg-error "no match" } > + auto v1 = views::lazy_split(std::initializer_list<char>{' ', ' '})(x); // { dg-error "deleted" } > + auto v2 = x | views::lazy_split(std::initializer_list<char>{' ', ' '}); // { dg-error "no match" } > } > > // { dg-prune-output "in requirements" } > diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/p2281.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/p2281.cc > index c916a5ea8b7..7950c43576d 100644 > --- a/libstdc++-v3/testsuite/std/ranges/adaptors/p2281.cc > +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/p2281.cc > @@ -34,7 +34,7 @@ void > test01() > { > auto split_into_strings = [] (auto p) { > - return views::split(p) | views::transform([](auto r){ > + return views::lazy_split(p) | views::transform([](auto r){ > return std::string(r.begin(), ranges::next(r.begin(), r.end())); > }); > }; > @@ -60,19 +60,19 @@ struct move_only_range > template<> > inline constexpr bool std::ranges::enable_view<move_only_range> = true; > > -template<auto split = views::split> > +template<auto lazy_split = views::lazy_split> > void > test02() > { > std::string_view s; > move_only_range p; > - static_assert(requires { s | split(std::move(p)); }); > - static_assert(requires { split(std::move(p))(s); }); > - static_assert(requires { split(std::move(p)) | views::all; }); > - static_assert(requires { views::all | split(std::move(p)); }); > - static_assert(!requires { split(p); }); > - static_assert(!requires { split(p) | views::all; }); > - static_assert(!requires { views::all | split(p); }); > + static_assert(requires { s | lazy_split(std::move(p)); }); > + static_assert(requires { lazy_split(std::move(p))(s); }); > + static_assert(requires { lazy_split(std::move(p)) | views::all; }); > + static_assert(requires { views::all | lazy_split(std::move(p)); }); > + static_assert(!requires { lazy_split(p); }); > + static_assert(!requires { lazy_split(p) | views::all; }); > + static_assert(!requires { views::all | lazy_split(p); }); > } > > int > diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/sizeof.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/sizeof.cc > index 80326f8bf21..219e2a61f07 100644 > --- a/libstdc++-v3/testsuite/std/ranges/adaptors/sizeof.cc > +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/sizeof.cc > @@ -46,7 +46,7 @@ static_assert(sizeof(ranges::take_while_view<V, decltype(pred_l)>) == 3*ptr); > static_assert(sizeof(ranges::drop_while_view<V, decltype(pred_l)>) == 3*ptr); > static_assert(sizeof(ranges::transform_view<V, decltype(func_l)>) == 3*ptr); > > -static_assert(sizeof(ranges::split_view<V, std::string_view>) == 4*ptr); > +static_assert(sizeof(ranges::lazy_split_view<V, std::string_view>) == 4*ptr); > > static_assert > (sizeof(ranges::reverse_view<ranges::filter_view<V, decltype(pred_l)>>) == 4*ptr); > diff --git a/libstdc++-v3/testsuite/std/ranges/p2259.cc b/libstdc++-v3/testsuite/std/ranges/p2259.cc > index 1b422e44f16..0ec7e21f657 100644 > --- a/libstdc++-v3/testsuite/std/ranges/p2259.cc > +++ b/libstdc++-v3/testsuite/std/ranges/p2259.cc > @@ -49,12 +49,12 @@ test01() > // Verify the changes to transform_view. > only_cxx20_input_range auto v2 = v0 | views::transform([](int& c) -> auto& { return c; }); > > - // Verify the changes to split_view. > - only_cxx20_input_range auto v3 = v0 | views::split(12); > + // Verify the changes to lazy_split_view. > + only_cxx20_input_range auto v3 = v0 | views::lazy_split(12); > static_assert(only_cxx20_input_range<decltype(*v3.begin())>); > > // Verify the changes to join_view. > - only_cxx20_input_range auto v4 = v0 | views::split(12) | views::join; > + only_cxx20_input_range auto v4 = v0 | views::lazy_split(12) | views::join; > > // Verify the changes to elements_view. > only_cxx20_input_range auto v5 > diff --git a/libstdc++-v3/testsuite/std/ranges/p2325.cc b/libstdc++-v3/testsuite/std/ranges/p2325.cc > index df6cde29e4d..4d075409026 100644 > --- a/libstdc++-v3/testsuite/std/ranges/p2325.cc > +++ b/libstdc++-v3/testsuite/std/ranges/p2325.cc > @@ -113,14 +113,14 @@ test07() > void > test08() > { > - // Verify split_view is conditionally default constructible. > - using type1 = ranges::split_view<ranges::ref_view<int[2]>, ranges::single_view<int>>; > + // Verify lazy_split_view is conditionally default constructible. > + using type1 = ranges::lazy_split_view<ranges::ref_view<int[2]>, ranges::single_view<int>>; > static_assert(!default_initializable<type1>); > - using type2 = ranges::split_view<ranges::single_view<int>, ranges::ref_view<int[2]>>; > + using type2 = ranges::lazy_split_view<ranges::single_view<int>, ranges::ref_view<int[2]>>; > static_assert(!default_initializable<type2>); > - using type3 = ranges::split_view<ranges::ref_view<int[2]>, ranges::ref_view<int[2]>>; > + using type3 = ranges::lazy_split_view<ranges::ref_view<int[2]>, ranges::ref_view<int[2]>>; > static_assert(!default_initializable<type3>); > - using type4 = ranges::split_view<ranges::single_view<int>, ranges::single_view<int>>; > + using type4 = ranges::lazy_split_view<ranges::single_view<int>, ranges::single_view<int>>; > static_assert(default_initializable<type4>); > } > > diff --git a/libstdc++-v3/testsuite/std/ranges/p2367.cc b/libstdc++-v3/testsuite/std/ranges/p2367.cc > index 40875233d88..5228b021602 100644 > --- a/libstdc++-v3/testsuite/std/ranges/p2367.cc > +++ b/libstdc++-v3/testsuite/std/ranges/p2367.cc > @@ -43,6 +43,6 @@ test01() > // Verify changes to views::drop. > auto v5 = views::drop(x, 0ull); > > - // Verify changes to views::split. > - auto v6 = views::split(x, 5u); > + // Verify changes to views::lazy_split. > + auto v6 = views::lazy_split(x, 5u); > } > -- > 2.32.0.93.g670b81a890 > ^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 4/5] libstdc++: Implement resolution of LWG 3478 as per P2210 2021-06-17 15:22 [PATCH 1/5] libstdc++: Implement P2325 changes to default-constructibilty of views Patrick Palka 2021-06-17 15:22 ` [PATCH 2/5] libstdc++: Move ranges algos used by <ranges> into ranges_util.h Patrick Palka 2021-06-17 15:22 ` [PATCH 3/5] libstdc++: Rename views::split to views::lazy_split as per P2210 Patrick Palka @ 2021-06-17 15:22 ` Patrick Palka 2021-06-18 21:57 ` Jonathan Wakely 2021-06-17 15:22 ` [PATCH 5/5] libstdc++: Implement new views::split " Patrick Palka 2021-06-17 18:19 ` [PATCH 1/5] libstdc++: Implement P2325 changes to default-constructibilty of views Jonathan Wakely 4 siblings, 1 reply; 11+ messages in thread From: Patrick Palka @ 2021-06-17 15:22 UTC (permalink / raw) To: gcc-patches; +Cc: libstdc++, Patrick Palka This implements the part of P2210R2 "Superior String Splitting" that resolves LWG 3478 for split_view (now named lazy_split_view). libstdc++-v3/ChangeLog: * include/std/ranges (lazy_split_view::_OuterIter::__at_end): Check _M_trailing_empty. (lazy_split_view::_OuterIter::_M_trailing_empty): Define this data member. (lazy_split_view::_OuterIter::operator++): Set _M_trailing_empty appropriately. (lazy_split_view::_OuterIter::operator==): Compare _M_trailing_empty. * testsuite/std/ranges/adaptors/100479.cc (test03): Expect two split parts instead of one. * testsuite/std/ranges/adaptors/lazy_split.cc (test11): New test. --- libstdc++-v3/include/std/ranges | 21 +++++++++++++++---- .../testsuite/std/ranges/adaptors/100479.cc | 6 +++--- .../std/ranges/adaptors/lazy_split.cc | 15 +++++++++++++ 3 files changed, 35 insertions(+), 7 deletions(-) diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges index cc1ef112ff1..78562924bee 100644 --- a/libstdc++-v3/include/std/ranges +++ b/libstdc++-v3/include/std/ranges @@ -2879,7 +2879,7 @@ namespace views::__adaptor constexpr bool __at_end() const - { return __current() == ranges::end(_M_parent->_M_base); } + { return __current() == ranges::end(_M_parent->_M_base) && !_M_trailing_empty; } // [range.lazy.split.outer] p1 // Many of the following specifications refer to the notional member @@ -2909,6 +2909,7 @@ namespace views::__adaptor [[no_unique_address]] __detail::__maybe_present_t<forward_range<_Vp>, iterator_t<_Base>> _M_current; + bool _M_trailing_empty = false; public: using iterator_concept = conditional_t<forward_range<_Base>, @@ -2977,7 +2978,10 @@ namespace views::__adaptor // 3505. lazy_split_view::outer-iterator::operator++ misspecified const auto __end = ranges::end(_M_parent->_M_base); if (__current() == __end) - return *this; + { + _M_trailing_empty = false; + return *this; + } const auto [__pbegin, __pend] = subrange{_M_parent->_M_pattern}; if (__pbegin == __pend) ++__current(); @@ -2986,7 +2990,11 @@ namespace views::__adaptor __current() = ranges::find(std::move(__current()), __end, *__pbegin); if (__current() != __end) - ++__current(); + { + ++__current(); + if (__current() == __end) + _M_trailing_empty = true; + } } else do @@ -2996,6 +3004,8 @@ namespace views::__adaptor if (__p == __pend) { __current() = __b; + if (__current() == __end) + _M_trailing_empty = true; break; } } while (++__current() != __end); @@ -3018,7 +3028,10 @@ namespace views::__adaptor friend constexpr bool operator==(const _OuterIter& __x, const _OuterIter& __y) requires forward_range<_Base> - { return __x._M_current == __y._M_current; } + { + return __x._M_current == __y._M_current + && __x._M_trailing_empty == __y._M_trailing_empty; + } friend constexpr bool operator==(const _OuterIter& __x, default_sentinel_t) diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/100479.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/100479.cc index 9899ff92c0b..b8c1e6f4f57 100644 --- a/libstdc++-v3/testsuite/std/ranges/adaptors/100479.cc +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/100479.cc @@ -95,11 +95,11 @@ test03() | views::drop_while([](auto) { return false; }) | views::filter([](auto) { return true; }); static_assert(ranges::forward_range<decltype(v)>); - VERIFY( ranges::next(v.begin()) == v.end() ); + VERIFY( ranges::distance(v) == 2 ); auto w = v; - VERIFY( ranges::next(w.begin()) == w.end() ); + VERIFY( ranges::distance(v) == 2 ); auto z = std::move(w); - VERIFY( ranges::next(z.begin()) == z.end() ); + VERIFY( ranges::distance(v) == 2 ); return true; } diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split.cc index 12844525d86..133e9a7025b 100644 --- a/libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split.cc +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split.cc @@ -193,6 +193,20 @@ test10() VERIFY( ranges::equal(v, (std::string_view[]){"x", "x"}) ); } +void +test11() +{ + // LWG 3478 + static_assert(ranges::distance(views::lazy_split("text"sv, "text"sv)) == 2); + static_assert(ranges::distance(views::lazy_split(" text "sv, ' ')) == 3); + static_assert(ranges::distance(views::lazy_split(" t e x t "sv, ' ')) == 6); + static_assert(ranges::distance(views::lazy_split(" text "sv, " "sv)) == 3); + static_assert(ranges::distance(views::lazy_split(" text "sv, " "sv)) == 4); + static_assert(ranges::distance(views::lazy_split(" text "sv, " "sv)) == 4); + static_assert(ranges::distance(views::lazy_split("t"sv, 't')) == 2); + static_assert(ranges::distance(views::lazy_split("text"sv, ""sv)) == 4); +} + int main() { @@ -206,4 +220,5 @@ main() test08(); test09(); test10(); + test11(); } -- 2.32.0.93.g670b81a890 ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 4/5] libstdc++: Implement resolution of LWG 3478 as per P2210 2021-06-17 15:22 ` [PATCH 4/5] libstdc++: Implement resolution of LWG 3478 " Patrick Palka @ 2021-06-18 21:57 ` Jonathan Wakely 0 siblings, 0 replies; 11+ messages in thread From: Jonathan Wakely @ 2021-06-18 21:57 UTC (permalink / raw) To: Patrick Palka; +Cc: gcc Patches, libstdc++ On Thu, 17 Jun 2021 at 16:51, Patrick Palka via Libstdc++ <libstdc++@gcc.gnu.org> wrote: > > This implements the part of P2210R2 "Superior String Splitting" that > resolves LWG 3478 for split_view (now named lazy_split_view). > > libstdc++-v3/ChangeLog: > > * include/std/ranges (lazy_split_view::_OuterIter::__at_end): > Check _M_trailing_empty. > (lazy_split_view::_OuterIter::_M_trailing_empty): Define this > data member. > (lazy_split_view::_OuterIter::operator++): Set _M_trailing_empty > appropriately. > (lazy_split_view::_OuterIter::operator==): Compare > _M_trailing_empty. > * testsuite/std/ranges/adaptors/100479.cc (test03): Expect two > split parts instead of one. > * testsuite/std/ranges/adaptors/lazy_split.cc (test11): New test. OK (for trunk only, I think). > --- > libstdc++-v3/include/std/ranges | 21 +++++++++++++++---- > .../testsuite/std/ranges/adaptors/100479.cc | 6 +++--- > .../std/ranges/adaptors/lazy_split.cc | 15 +++++++++++++ > 3 files changed, 35 insertions(+), 7 deletions(-) > > diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges > index cc1ef112ff1..78562924bee 100644 > --- a/libstdc++-v3/include/std/ranges > +++ b/libstdc++-v3/include/std/ranges > @@ -2879,7 +2879,7 @@ namespace views::__adaptor > > constexpr bool > __at_end() const > - { return __current() == ranges::end(_M_parent->_M_base); } > + { return __current() == ranges::end(_M_parent->_M_base) && !_M_trailing_empty; } > > // [range.lazy.split.outer] p1 > // Many of the following specifications refer to the notional member > @@ -2909,6 +2909,7 @@ namespace views::__adaptor > [[no_unique_address]] > __detail::__maybe_present_t<forward_range<_Vp>, > iterator_t<_Base>> _M_current; > + bool _M_trailing_empty = false; > > public: > using iterator_concept = conditional_t<forward_range<_Base>, > @@ -2977,7 +2978,10 @@ namespace views::__adaptor > // 3505. lazy_split_view::outer-iterator::operator++ misspecified > const auto __end = ranges::end(_M_parent->_M_base); > if (__current() == __end) > - return *this; > + { > + _M_trailing_empty = false; > + return *this; > + } > const auto [__pbegin, __pend] = subrange{_M_parent->_M_pattern}; > if (__pbegin == __pend) > ++__current(); > @@ -2986,7 +2990,11 @@ namespace views::__adaptor > __current() = ranges::find(std::move(__current()), __end, > *__pbegin); > if (__current() != __end) > - ++__current(); > + { > + ++__current(); > + if (__current() == __end) > + _M_trailing_empty = true; > + } > } > else > do > @@ -2996,6 +3004,8 @@ namespace views::__adaptor > if (__p == __pend) > { > __current() = __b; > + if (__current() == __end) > + _M_trailing_empty = true; > break; > } > } while (++__current() != __end); > @@ -3018,7 +3028,10 @@ namespace views::__adaptor > friend constexpr bool > operator==(const _OuterIter& __x, const _OuterIter& __y) > requires forward_range<_Base> > - { return __x._M_current == __y._M_current; } > + { > + return __x._M_current == __y._M_current > + && __x._M_trailing_empty == __y._M_trailing_empty; > + } > > friend constexpr bool > operator==(const _OuterIter& __x, default_sentinel_t) > diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/100479.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/100479.cc > index 9899ff92c0b..b8c1e6f4f57 100644 > --- a/libstdc++-v3/testsuite/std/ranges/adaptors/100479.cc > +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/100479.cc > @@ -95,11 +95,11 @@ test03() > | views::drop_while([](auto) { return false; }) > | views::filter([](auto) { return true; }); > static_assert(ranges::forward_range<decltype(v)>); > - VERIFY( ranges::next(v.begin()) == v.end() ); > + VERIFY( ranges::distance(v) == 2 ); > auto w = v; > - VERIFY( ranges::next(w.begin()) == w.end() ); > + VERIFY( ranges::distance(v) == 2 ); > auto z = std::move(w); > - VERIFY( ranges::next(z.begin()) == z.end() ); > + VERIFY( ranges::distance(v) == 2 ); > return true; > } > > diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split.cc > index 12844525d86..133e9a7025b 100644 > --- a/libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split.cc > +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split.cc > @@ -193,6 +193,20 @@ test10() > VERIFY( ranges::equal(v, (std::string_view[]){"x", "x"}) ); > } > > +void > +test11() > +{ > + // LWG 3478 > + static_assert(ranges::distance(views::lazy_split("text"sv, "text"sv)) == 2); > + static_assert(ranges::distance(views::lazy_split(" text "sv, ' ')) == 3); > + static_assert(ranges::distance(views::lazy_split(" t e x t "sv, ' ')) == 6); > + static_assert(ranges::distance(views::lazy_split(" text "sv, " "sv)) == 3); > + static_assert(ranges::distance(views::lazy_split(" text "sv, " "sv)) == 4); > + static_assert(ranges::distance(views::lazy_split(" text "sv, " "sv)) == 4); > + static_assert(ranges::distance(views::lazy_split("t"sv, 't')) == 2); > + static_assert(ranges::distance(views::lazy_split("text"sv, ""sv)) == 4); > +} > + > int > main() > { > @@ -206,4 +220,5 @@ main() > test08(); > test09(); > test10(); > + test11(); > } > -- > 2.32.0.93.g670b81a890 > ^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 5/5] libstdc++: Implement new views::split as per P2210 2021-06-17 15:22 [PATCH 1/5] libstdc++: Implement P2325 changes to default-constructibilty of views Patrick Palka ` (2 preceding siblings ...) 2021-06-17 15:22 ` [PATCH 4/5] libstdc++: Implement resolution of LWG 3478 " Patrick Palka @ 2021-06-17 15:22 ` Patrick Palka 2021-06-18 21:58 ` Jonathan Wakely 2021-06-17 18:19 ` [PATCH 1/5] libstdc++: Implement P2325 changes to default-constructibilty of views Jonathan Wakely 4 siblings, 1 reply; 11+ messages in thread From: Patrick Palka @ 2021-06-17 15:22 UTC (permalink / raw) To: gcc-patches; +Cc: libstdc++, Patrick Palka This implements the new views::split as specified by P2210R2 "Superior string splitting". libstdc++-v3/ChangeLog: * include/std/ranges (__non_propagating_cache::operator bool): Define. (split_view): Define as per P2210. (views::__detail::__can_split_view): Define. (views::_Split, views::Split): Define. * testsuite/std/ranges/adaptors/100577.cc (test01, test02): Test views::split. * testsuite/std/ranges/adaptors/split.cc: New test. * testsuite/std/ranges/p2325.cc (test08a): New test. * testsuite/std/ranges/p2367.cc (test01): Test views::split. --- libstdc++-v3/include/std/ranges | 205 ++++++++++++++++++ .../testsuite/std/ranges/adaptors/100577.cc | 16 +- .../testsuite/std/ranges/adaptors/split.cc | 196 +++++++++++++++++ libstdc++-v3/testsuite/std/ranges/p2325.cc | 14 ++ libstdc++-v3/testsuite/std/ranges/p2367.cc | 1 + 5 files changed, 430 insertions(+), 2 deletions(-) create mode 100644 libstdc++-v3/testsuite/std/ranges/adaptors/split.cc diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges index 78562924bee..42278f128b8 100644 --- a/libstdc++-v3/include/std/ranges +++ b/libstdc++-v3/include/std/ranges @@ -1195,6 +1195,10 @@ namespace views::__adaptor return *this; } + constexpr explicit + operator bool() const noexcept + { return this->_M_is_engaged(); } + constexpr _Tp& operator*() noexcept { return this->_M_get(); } @@ -3278,6 +3282,207 @@ namespace views::__adaptor inline constexpr _LazySplit lazy_split; } // namespace views + template<forward_range _Vp, forward_range _Pattern> + requires view<_Vp> && view<_Pattern> + && indirectly_comparable<iterator_t<_Vp>, iterator_t<_Pattern>, + ranges::equal_to> + class split_view : public view_interface<split_view<_Vp, _Pattern>> + { + private: + _Pattern _M_pattern = _Pattern(); + __detail::__non_propagating_cache<subrange<iterator_t<_Vp>>> _M_cached_begin; + _Vp _M_base = _Vp(); + + struct _Iterator; + struct _Sentinel; + + public: + split_view() requires (default_initializable<_Vp> + && default_initializable<_Pattern>) + = default; + + constexpr + split_view(_Vp __base, _Pattern __pattern) + : _M_pattern(std::move(__pattern)), + _M_base(std::move(__base)) + { } + + template<forward_range _Range> + requires constructible_from<_Vp, views::all_t<_Range>> + && constructible_from<_Pattern, single_view<range_value_t<_Range>>> + constexpr + split_view(_Range&& __r, range_value_t<_Range> __e) + : _M_pattern(views::single(__e)), + _M_base(views::all(std::forward<_Range>(__r))) + { } + + constexpr _Vp + base() const& requires copyable<_Vp> + { return _M_base; } + + constexpr _Vp + base() && + { return std::move(_M_base); } + + constexpr _Iterator + begin() + { + if (!_M_cached_begin) + _M_cached_begin = _M_find_next(ranges::begin(_M_base)); + return {this, ranges::begin(_M_base), *_M_cached_begin}; + } + + constexpr auto + end() + { + if constexpr (common_range<_Vp>) + return _Iterator{this, ranges::end(_M_base), {}}; + else + return _Sentinel{this}; + } + + constexpr subrange<iterator_t<_Vp>> + _M_find_next(iterator_t<_Vp> __it) + { + auto [__b, __e] = ranges::search(subrange(__it, ranges::end(_M_base)), _M_pattern); + if (__b != ranges::end(_M_base) && ranges::empty(_M_pattern)) + { + ++__b; + ++__e; + } + return {__b, __e}; + } + + private: + struct _Iterator + { + private: + split_view* _M_parent = nullptr; + iterator_t<_Vp> _M_cur = iterator_t<_Vp>(); + subrange<iterator_t<_Vp>> _M_next = subrange<iterator_t<_Vp>>(); + bool _M_trailing_empty = false; + + public: + using iterator_concept = forward_iterator_tag; + using iterator_category = input_iterator_tag; + using value_type = subrange<iterator_t<_Vp>>; + using difference_type = range_difference_t<_Vp>; + + _Iterator() requires default_initializable<iterator_t<_Vp>> = default; + + constexpr + _Iterator(split_view* __parent, + iterator_t<_Vp> __current, + subrange<iterator_t<_Vp>> __next) + : _M_parent(__parent), + _M_cur(std::move(__current)), + _M_next(std::move(__next)) + { } + + constexpr iterator_t<_Vp> + base() const + { return _M_cur; } + + constexpr value_type + operator*() const + { return {_M_cur, _M_next.begin()}; } + + constexpr _Iterator& + operator++() + { + _M_cur = _M_next.begin(); + if (_M_cur != ranges::end(_M_parent->_M_base)) + { + _M_cur = _M_next.end(); + if (_M_cur == ranges::end(_M_parent->_M_base)) + { + _M_trailing_empty = true; + _M_next = {_M_cur, _M_cur}; + } + else + _M_next = _M_parent->_M_find_next(_M_cur); + } + else + _M_trailing_empty = false; + return *this; + } + + constexpr _Iterator + operator++(int) + { + auto __tmp = *this; + ++*this; + return __tmp; + } + + friend constexpr bool + operator==(const _Iterator& __x, const _Iterator& __y) + { + return __x._M_cur == __y._M_cur + && __x._M_trailing_empty == __y._M_trailing_empty; + } + + friend struct _Sentinel; + }; + + struct _Sentinel + { + private: + sentinel_t<_Vp> _M_end = sentinel_t<_Vp>(); + + constexpr bool + _M_equal(const _Iterator& __x) const + { return __x._M_cur == _M_end && !__x._M_trailing_empty; } + + public: + constexpr explicit + _Sentinel(split_view* __parent) + : _M_end(ranges::end(__parent->_M_base)) + { } + + friend constexpr bool + operator==(const _Iterator& __x, const _Sentinel& __y) + { return __y._M_equal(__x); } + }; + }; + + template<typename _Range, typename _Pattern> + split_view(_Range&&, _Pattern&&) + -> split_view<views::all_t<_Range>, views::all_t<_Pattern>>; + + template<forward_range _Range> + split_view(_Range&&, range_value_t<_Range>) + -> split_view<views::all_t<_Range>, single_view<range_value_t<_Range>>>; + + namespace views + { + namespace __detail + { + template<typename _Range, typename _Pattern> + concept __can_split_view + = requires { split_view(std::declval<_Range>(), std::declval<_Pattern>()); }; + } // namespace __detail + + struct _Split : __adaptor::_RangeAdaptor<_Split> + { + template<viewable_range _Range, typename _Pattern> + requires __detail::__can_split_view<_Range, _Pattern> + constexpr auto + operator()(_Range&& __r, _Pattern&& __f) const + { + return split_view(std::forward<_Range>(__r), std::forward<_Pattern>(__f)); + } + + using _RangeAdaptor<_Split>::operator(); + static constexpr int _S_arity = 2; + template<typename _Pattern> + static constexpr bool _S_has_simple_extra_args + = _LazySplit::_S_has_simple_extra_args<_Pattern>; + }; + + inline constexpr _Split split; + } // namespace views + namespace views { struct _Counted diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/100577.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/100577.cc index 5ef7f3f59a7..81f2a62cfaa 100644 --- a/libstdc++-v3/testsuite/std/ranges/adaptors/100577.cc +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/100577.cc @@ -42,8 +42,6 @@ test01() static_assert(__adaptor_has_simple_extra_args<decltype(views::lazy_split), char>); static_assert(!__adaptor_has_simple_extra_args<decltype(views::lazy_split), std::string>); - // Verify all adaptor closures except for views::lazy_split(pattern) have a simple - // operator(). using views::__adaptor::__closure_has_simple_call_op; __closure_has_simple_call_op auto a00 = views::all; __closure_has_simple_call_op auto a01 = views::transform(std::identity{}); @@ -57,6 +55,7 @@ test01() __closure_has_simple_call_op auto a09 = views::reverse; __closure_has_simple_call_op auto a10 = views::keys; __closure_has_simple_call_op auto a11 = views::lazy_split(' '); + __closure_has_simple_call_op auto a11a = views::split(' '); // Verify composition of simple closures is simple. __closure_has_simple_call_op auto b = (a00 | a01) | (a02 | a03) | (a04 | a05 | a06) | (a07 | a08 | a09 | a10) | a11; @@ -67,6 +66,12 @@ test01() static_assert(!__closure_has_simple_call_op<decltype(a12)>); static_assert(!__closure_has_simple_call_op<decltype(a12 | a00)>); static_assert(!__closure_has_simple_call_op<decltype(a00 | a12)>); + + // Likewise views::split(non_view_range). + auto a12a = views::split(s); + static_assert(!__closure_has_simple_call_op<decltype(a12a)>); + static_assert(!__closure_has_simple_call_op<decltype(a12a | a00)>); + static_assert(!__closure_has_simple_call_op<decltype(a00 | a12a)>); } void @@ -98,6 +103,13 @@ test02() auto a1 = a0 | views::all; a1(x); // { dg-error "no match" } + views::lazy_split(badarg)(x); // { dg-error "deleted function" } + (views::lazy_split(badarg) | views::all)(x); // { dg-error "deleted function" } + auto a0a = views::split(badarg); + a0a(x); // { dg-error "no match" }; + auto a1a = a0a | views::all; + a1a(x); // { dg-error "no match" } + views::take(badarg)(x); // { dg-error "deleted" } views::drop(badarg)(x); // { dg-error "deleted" } (views::take(badarg) | views::all)(x); // { dg-error "deleted" } diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/split.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/split.cc new file mode 100644 index 00000000000..9e6726cd07f --- /dev/null +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/split.cc @@ -0,0 +1,196 @@ +// Copyright (C) 2020-2021 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// <http://www.gnu.org/licenses/>. + +// { dg-options "-std=gnu++2a" } +// { dg-do run { target c++2a } } + +#include <algorithm> +#include <ranges> +#include <string> +#include <string_view> +#include <testsuite_hooks.h> +#include <testsuite_iterators.h> + +using __gnu_test::test_range; +using __gnu_test::forward_iterator_wrapper; +using __gnu_test::input_iterator_wrapper; + +namespace ranges = std::ranges; +namespace views = std::ranges::views; + +using namespace std::literals; + +void +test01() +{ + auto from_chars = [] (auto v) { + return std::stoi(std::string(v.data(), v.data() + v.size())); + }; + auto ints = "1.2.3.4"sv + | views::split('.') + | views::transform(from_chars); + VERIFY( ranges::equal(ints, (int[]){1,2,3,4}) ); +} + +// The following testcases were adapted from lazy_split.cc. +namespace from_lazy_split_cc +{ +void +test01() +{ + auto x = "the quick brown fox"sv; + auto p = std::string{" "}; + auto v = x | views::split(views::all(p)); // views::all is needed here after P2281. + auto i = v.begin(); + VERIFY( ranges::equal(*i++, "the"sv) ); + VERIFY( ranges::equal(*i++, "quick"sv) ); + VERIFY( ranges::equal(*i++, "brown"sv) ); + VERIFY( ranges::equal(*i++, "fox"sv) ); + VERIFY( i == v.end() ); +} + +void +test02() +{ + auto x = "the quick brown fox"sv; + auto v = x | views::split(' '); + auto i = v.begin(); + VERIFY( ranges::equal(*i++, "the"sv) ); + VERIFY( ranges::equal(*i++, "quick"sv) ); + VERIFY( ranges::equal(*i++, "brown"sv) ); + VERIFY( ranges::equal(*i++, "fox"sv) ); + VERIFY( i == v.end() ); +} + +void +test03() +{ + char x[] = "the quick brown fox"; + test_range<char, forward_iterator_wrapper> rx(x, x+sizeof(x)-1); + auto v = rx | views::split(' '); + auto i = v.begin(); + VERIFY( ranges::equal(*i++, "the"sv) ); + VERIFY( ranges::equal(*i++, "quick"sv) ); + VERIFY( ranges::equal(*i++, "brown"sv) ); + VERIFY( ranges::equal(*i++, "fox"sv) ); + VERIFY( i == v.end() ); +} + +void +test04() +{ + auto x = "the quick brown fox"sv; + std::initializer_list<char> p = {' ', ' '}; + static_assert(!ranges::view<decltype(p)>); + static_assert(std::same_as<decltype(p | views::all), + ranges::ref_view<decltype(p)>>); + auto v = x | views::split(views::all(p)); // views::all is needed here after P2281. + auto i = v.begin(); + VERIFY( ranges::equal(*i++, "the"sv) ); + VERIFY( ranges::equal(*i++, "quick"sv) ); + VERIFY( ranges::equal(*i++, "brown"sv) ); + VERIFY( ranges::equal(*i++, "fox"sv) ); + VERIFY( i == v.end() ); +} + +void +test05() +{ + auto as_string = [](ranges::view auto rng) { + auto in = rng | views::common; + return std::string(in.begin(), in.end()); + }; + std::string str + = "Now is the time for all good men to come to the aid of their county."; + auto rng + = str | views::split(' ') | views::transform(as_string) | views::common; + std::vector<std::string> words(rng.begin(), rng.end()); + auto not_space_p = [](char c) { return c != ' '; }; + VERIFY( ranges::equal(words | views::join, + str | views::filter(not_space_p)) ); +} + +template<auto split = views::split> +void +test06() +{ + // Verify SFINAE behavior. + std::string s, p; + static_assert(!requires { split(); }); + static_assert(!requires { split(s, p, 0); }); + static_assert(!requires { split(p)(); }); + static_assert(!requires { s | split; }); + + static_assert(!requires { s | split(p); }); + static_assert(!requires { split(p)(s); }); + static_assert(!requires { s | (split(p) | views::all); }); + static_assert(!requires { (split(p) | views::all)(s); }); + + static_assert(requires { s | split(views::all(p)); }); + static_assert(requires { split(views::all(p))(s); }); + static_assert(requires { s | (split(views::all(p)) | views::all); }); + static_assert(requires { (split(views::all(p)) | views::all)(s); }); + + auto adapt = split(p); + static_assert(requires { s | adapt; }); + static_assert(requires { adapt(s); }); + + auto adapt2 = split(p) | views::all; + static_assert(requires { s | adapt2; }); + static_assert(requires { adapt2(s); }); +} + +void +test10() +{ + // LWG 3505 + auto to_string = [] (auto r) { + return std::string(r.begin(), ranges::next(r.begin(), r.end())); + }; + auto v = "xxyx"sv | views::split("xy"sv) | views::transform(to_string); + VERIFY( ranges::equal(v, (std::string_view[]){"x", "x"}) ); +} + +void +test11() +{ + // LWG 3478 + static_assert(ranges::distance(views::split("text"sv, "text"sv)) == 2); + static_assert(ranges::distance(views::split(" text "sv, ' ')) == 3); + static_assert(ranges::distance(views::split(" t e x t "sv, ' ')) == 6); + static_assert(ranges::distance(views::split(" text "sv, " "sv)) == 3); + static_assert(ranges::distance(views::split(" text "sv, " "sv)) == 4); + static_assert(ranges::distance(views::split(" text "sv, " "sv)) == 4); + static_assert(ranges::distance(views::split("t"sv, 't')) == 2); + static_assert(ranges::distance(views::split("text"sv, ""sv)) == 4); +} +} // namespace from_lazy_split_cc + +int +main() +{ + test01(); + + from_lazy_split_cc::test01(); + from_lazy_split_cc::test02(); + from_lazy_split_cc::test03(); + from_lazy_split_cc::test04(); + from_lazy_split_cc::test05(); + from_lazy_split_cc::test06(); + from_lazy_split_cc::test10(); + from_lazy_split_cc::test11(); +} diff --git a/libstdc++-v3/testsuite/std/ranges/p2325.cc b/libstdc++-v3/testsuite/std/ranges/p2325.cc index 4d075409026..d2ebe9af863 100644 --- a/libstdc++-v3/testsuite/std/ranges/p2325.cc +++ b/libstdc++-v3/testsuite/std/ranges/p2325.cc @@ -124,6 +124,20 @@ test08() static_assert(default_initializable<type4>); } +void +test08a() +{ + // Verify split_view is conditionally default constructible. + using type1 = ranges::split_view<ranges::ref_view<int[2]>, ranges::single_view<int>>; + static_assert(!default_initializable<type1>); + using type2 = ranges::split_view<ranges::single_view<int>, ranges::ref_view<int[2]>>; + static_assert(!default_initializable<type2>); + using type3 = ranges::split_view<ranges::ref_view<int[2]>, ranges::ref_view<int[2]>>; + static_assert(!default_initializable<type3>); + using type4 = ranges::split_view<ranges::single_view<int>, ranges::single_view<int>>; + static_assert(default_initializable<type4>); +} + void test09() { diff --git a/libstdc++-v3/testsuite/std/ranges/p2367.cc b/libstdc++-v3/testsuite/std/ranges/p2367.cc index 5228b021602..70a0304593f 100644 --- a/libstdc++-v3/testsuite/std/ranges/p2367.cc +++ b/libstdc++-v3/testsuite/std/ranges/p2367.cc @@ -45,4 +45,5 @@ test01() // Verify changes to views::lazy_split. auto v6 = views::lazy_split(x, 5u); + auto v7 = views::split(x, 5u); } -- 2.32.0.93.g670b81a890 ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 5/5] libstdc++: Implement new views::split as per P2210 2021-06-17 15:22 ` [PATCH 5/5] libstdc++: Implement new views::split " Patrick Palka @ 2021-06-18 21:58 ` Jonathan Wakely 0 siblings, 0 replies; 11+ messages in thread From: Jonathan Wakely @ 2021-06-18 21:58 UTC (permalink / raw) To: Patrick Palka; +Cc: gcc Patches, libstdc++ On Thu, 17 Jun 2021 at 16:59, Patrick Palka via Libstdc++ <libstdc++@gcc.gnu.org> wrote: > > This implements the new views::split as specified by P2210R2 "Superior > string splitting". > > libstdc++-v3/ChangeLog: > > * include/std/ranges (__non_propagating_cache::operator bool): > Define. > (split_view): Define as per P2210. > (views::__detail::__can_split_view): Define. > (views::_Split, views::Split): Define. > * testsuite/std/ranges/adaptors/100577.cc (test01, test02): > Test views::split. > * testsuite/std/ranges/adaptors/split.cc: New test. > * testsuite/std/ranges/p2325.cc (test08a): New test. > * testsuite/std/ranges/p2367.cc (test01): Test views::split. OK > --- > libstdc++-v3/include/std/ranges | 205 ++++++++++++++++++ > .../testsuite/std/ranges/adaptors/100577.cc | 16 +- > .../testsuite/std/ranges/adaptors/split.cc | 196 +++++++++++++++++ > libstdc++-v3/testsuite/std/ranges/p2325.cc | 14 ++ > libstdc++-v3/testsuite/std/ranges/p2367.cc | 1 + > 5 files changed, 430 insertions(+), 2 deletions(-) > create mode 100644 libstdc++-v3/testsuite/std/ranges/adaptors/split.cc > > diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges > index 78562924bee..42278f128b8 100644 > --- a/libstdc++-v3/include/std/ranges > +++ b/libstdc++-v3/include/std/ranges > @@ -1195,6 +1195,10 @@ namespace views::__adaptor > return *this; > } > > + constexpr explicit > + operator bool() const noexcept > + { return this->_M_is_engaged(); } > + > constexpr _Tp& > operator*() noexcept > { return this->_M_get(); } > @@ -3278,6 +3282,207 @@ namespace views::__adaptor > inline constexpr _LazySplit lazy_split; > } // namespace views > > + template<forward_range _Vp, forward_range _Pattern> > + requires view<_Vp> && view<_Pattern> > + && indirectly_comparable<iterator_t<_Vp>, iterator_t<_Pattern>, > + ranges::equal_to> > + class split_view : public view_interface<split_view<_Vp, _Pattern>> > + { > + private: > + _Pattern _M_pattern = _Pattern(); > + __detail::__non_propagating_cache<subrange<iterator_t<_Vp>>> _M_cached_begin; > + _Vp _M_base = _Vp(); > + > + struct _Iterator; > + struct _Sentinel; > + > + public: > + split_view() requires (default_initializable<_Vp> > + && default_initializable<_Pattern>) > + = default; > + > + constexpr > + split_view(_Vp __base, _Pattern __pattern) > + : _M_pattern(std::move(__pattern)), > + _M_base(std::move(__base)) > + { } > + > + template<forward_range _Range> > + requires constructible_from<_Vp, views::all_t<_Range>> > + && constructible_from<_Pattern, single_view<range_value_t<_Range>>> > + constexpr > + split_view(_Range&& __r, range_value_t<_Range> __e) > + : _M_pattern(views::single(__e)), > + _M_base(views::all(std::forward<_Range>(__r))) > + { } > + > + constexpr _Vp > + base() const& requires copyable<_Vp> > + { return _M_base; } > + > + constexpr _Vp > + base() && > + { return std::move(_M_base); } > + > + constexpr _Iterator > + begin() > + { > + if (!_M_cached_begin) > + _M_cached_begin = _M_find_next(ranges::begin(_M_base)); > + return {this, ranges::begin(_M_base), *_M_cached_begin}; > + } > + > + constexpr auto > + end() > + { > + if constexpr (common_range<_Vp>) > + return _Iterator{this, ranges::end(_M_base), {}}; > + else > + return _Sentinel{this}; > + } > + > + constexpr subrange<iterator_t<_Vp>> > + _M_find_next(iterator_t<_Vp> __it) > + { > + auto [__b, __e] = ranges::search(subrange(__it, ranges::end(_M_base)), _M_pattern); > + if (__b != ranges::end(_M_base) && ranges::empty(_M_pattern)) > + { > + ++__b; > + ++__e; > + } > + return {__b, __e}; > + } > + > + private: > + struct _Iterator > + { > + private: > + split_view* _M_parent = nullptr; > + iterator_t<_Vp> _M_cur = iterator_t<_Vp>(); > + subrange<iterator_t<_Vp>> _M_next = subrange<iterator_t<_Vp>>(); > + bool _M_trailing_empty = false; > + > + public: > + using iterator_concept = forward_iterator_tag; > + using iterator_category = input_iterator_tag; > + using value_type = subrange<iterator_t<_Vp>>; > + using difference_type = range_difference_t<_Vp>; > + > + _Iterator() requires default_initializable<iterator_t<_Vp>> = default; > + > + constexpr > + _Iterator(split_view* __parent, > + iterator_t<_Vp> __current, > + subrange<iterator_t<_Vp>> __next) > + : _M_parent(__parent), > + _M_cur(std::move(__current)), > + _M_next(std::move(__next)) > + { } > + > + constexpr iterator_t<_Vp> > + base() const > + { return _M_cur; } > + > + constexpr value_type > + operator*() const > + { return {_M_cur, _M_next.begin()}; } > + > + constexpr _Iterator& > + operator++() > + { > + _M_cur = _M_next.begin(); > + if (_M_cur != ranges::end(_M_parent->_M_base)) > + { > + _M_cur = _M_next.end(); > + if (_M_cur == ranges::end(_M_parent->_M_base)) > + { > + _M_trailing_empty = true; > + _M_next = {_M_cur, _M_cur}; > + } > + else > + _M_next = _M_parent->_M_find_next(_M_cur); > + } > + else > + _M_trailing_empty = false; > + return *this; > + } > + > + constexpr _Iterator > + operator++(int) > + { > + auto __tmp = *this; > + ++*this; > + return __tmp; > + } > + > + friend constexpr bool > + operator==(const _Iterator& __x, const _Iterator& __y) > + { > + return __x._M_cur == __y._M_cur > + && __x._M_trailing_empty == __y._M_trailing_empty; > + } > + > + friend struct _Sentinel; > + }; > + > + struct _Sentinel > + { > + private: > + sentinel_t<_Vp> _M_end = sentinel_t<_Vp>(); > + > + constexpr bool > + _M_equal(const _Iterator& __x) const > + { return __x._M_cur == _M_end && !__x._M_trailing_empty; } > + > + public: > + constexpr explicit > + _Sentinel(split_view* __parent) > + : _M_end(ranges::end(__parent->_M_base)) > + { } > + > + friend constexpr bool > + operator==(const _Iterator& __x, const _Sentinel& __y) > + { return __y._M_equal(__x); } > + }; > + }; > + > + template<typename _Range, typename _Pattern> > + split_view(_Range&&, _Pattern&&) > + -> split_view<views::all_t<_Range>, views::all_t<_Pattern>>; > + > + template<forward_range _Range> > + split_view(_Range&&, range_value_t<_Range>) > + -> split_view<views::all_t<_Range>, single_view<range_value_t<_Range>>>; > + > + namespace views > + { > + namespace __detail > + { > + template<typename _Range, typename _Pattern> > + concept __can_split_view > + = requires { split_view(std::declval<_Range>(), std::declval<_Pattern>()); }; > + } // namespace __detail > + > + struct _Split : __adaptor::_RangeAdaptor<_Split> > + { > + template<viewable_range _Range, typename _Pattern> > + requires __detail::__can_split_view<_Range, _Pattern> > + constexpr auto > + operator()(_Range&& __r, _Pattern&& __f) const > + { > + return split_view(std::forward<_Range>(__r), std::forward<_Pattern>(__f)); > + } > + > + using _RangeAdaptor<_Split>::operator(); > + static constexpr int _S_arity = 2; > + template<typename _Pattern> > + static constexpr bool _S_has_simple_extra_args > + = _LazySplit::_S_has_simple_extra_args<_Pattern>; > + }; > + > + inline constexpr _Split split; > + } // namespace views > + > namespace views > { > struct _Counted > diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/100577.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/100577.cc > index 5ef7f3f59a7..81f2a62cfaa 100644 > --- a/libstdc++-v3/testsuite/std/ranges/adaptors/100577.cc > +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/100577.cc > @@ -42,8 +42,6 @@ test01() > static_assert(__adaptor_has_simple_extra_args<decltype(views::lazy_split), char>); > static_assert(!__adaptor_has_simple_extra_args<decltype(views::lazy_split), std::string>); > > - // Verify all adaptor closures except for views::lazy_split(pattern) have a simple > - // operator(). > using views::__adaptor::__closure_has_simple_call_op; > __closure_has_simple_call_op auto a00 = views::all; > __closure_has_simple_call_op auto a01 = views::transform(std::identity{}); > @@ -57,6 +55,7 @@ test01() > __closure_has_simple_call_op auto a09 = views::reverse; > __closure_has_simple_call_op auto a10 = views::keys; > __closure_has_simple_call_op auto a11 = views::lazy_split(' '); > + __closure_has_simple_call_op auto a11a = views::split(' '); > // Verify composition of simple closures is simple. > __closure_has_simple_call_op auto b > = (a00 | a01) | (a02 | a03) | (a04 | a05 | a06) | (a07 | a08 | a09 | a10) | a11; > @@ -67,6 +66,12 @@ test01() > static_assert(!__closure_has_simple_call_op<decltype(a12)>); > static_assert(!__closure_has_simple_call_op<decltype(a12 | a00)>); > static_assert(!__closure_has_simple_call_op<decltype(a00 | a12)>); > + > + // Likewise views::split(non_view_range). > + auto a12a = views::split(s); > + static_assert(!__closure_has_simple_call_op<decltype(a12a)>); > + static_assert(!__closure_has_simple_call_op<decltype(a12a | a00)>); > + static_assert(!__closure_has_simple_call_op<decltype(a00 | a12a)>); > } > > void > @@ -98,6 +103,13 @@ test02() > auto a1 = a0 | views::all; > a1(x); // { dg-error "no match" } > > + views::lazy_split(badarg)(x); // { dg-error "deleted function" } > + (views::lazy_split(badarg) | views::all)(x); // { dg-error "deleted function" } > + auto a0a = views::split(badarg); > + a0a(x); // { dg-error "no match" }; > + auto a1a = a0a | views::all; > + a1a(x); // { dg-error "no match" } > + > views::take(badarg)(x); // { dg-error "deleted" } > views::drop(badarg)(x); // { dg-error "deleted" } > (views::take(badarg) | views::all)(x); // { dg-error "deleted" } > diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/split.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/split.cc > new file mode 100644 > index 00000000000..9e6726cd07f > --- /dev/null > +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/split.cc > @@ -0,0 +1,196 @@ > +// Copyright (C) 2020-2021 Free Software Foundation, Inc. > +// > +// This file is part of the GNU ISO C++ Library. This library is free > +// software; you can redistribute it and/or modify it under the > +// terms of the GNU General Public License as published by the > +// Free Software Foundation; either version 3, or (at your option) > +// any later version. > + > +// This library is distributed in the hope that it will be useful, > +// but WITHOUT ANY WARRANTY; without even the implied warranty of > +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > +// GNU General Public License for more details. > + > +// You should have received a copy of the GNU General Public License along > +// with this library; see the file COPYING3. If not see > +// <http://www.gnu.org/licenses/>. > + > +// { dg-options "-std=gnu++2a" } > +// { dg-do run { target c++2a } } > + > +#include <algorithm> > +#include <ranges> > +#include <string> > +#include <string_view> > +#include <testsuite_hooks.h> > +#include <testsuite_iterators.h> > + > +using __gnu_test::test_range; > +using __gnu_test::forward_iterator_wrapper; > +using __gnu_test::input_iterator_wrapper; > + > +namespace ranges = std::ranges; > +namespace views = std::ranges::views; > + > +using namespace std::literals; > + > +void > +test01() > +{ > + auto from_chars = [] (auto v) { > + return std::stoi(std::string(v.data(), v.data() + v.size())); > + }; > + auto ints = "1.2.3.4"sv > + | views::split('.') > + | views::transform(from_chars); > + VERIFY( ranges::equal(ints, (int[]){1,2,3,4}) ); > +} > + > +// The following testcases were adapted from lazy_split.cc. > +namespace from_lazy_split_cc > +{ > +void > +test01() > +{ > + auto x = "the quick brown fox"sv; > + auto p = std::string{" "}; > + auto v = x | views::split(views::all(p)); // views::all is needed here after P2281. > + auto i = v.begin(); > + VERIFY( ranges::equal(*i++, "the"sv) ); > + VERIFY( ranges::equal(*i++, "quick"sv) ); > + VERIFY( ranges::equal(*i++, "brown"sv) ); > + VERIFY( ranges::equal(*i++, "fox"sv) ); > + VERIFY( i == v.end() ); > +} > + > +void > +test02() > +{ > + auto x = "the quick brown fox"sv; > + auto v = x | views::split(' '); > + auto i = v.begin(); > + VERIFY( ranges::equal(*i++, "the"sv) ); > + VERIFY( ranges::equal(*i++, "quick"sv) ); > + VERIFY( ranges::equal(*i++, "brown"sv) ); > + VERIFY( ranges::equal(*i++, "fox"sv) ); > + VERIFY( i == v.end() ); > +} > + > +void > +test03() > +{ > + char x[] = "the quick brown fox"; > + test_range<char, forward_iterator_wrapper> rx(x, x+sizeof(x)-1); > + auto v = rx | views::split(' '); > + auto i = v.begin(); > + VERIFY( ranges::equal(*i++, "the"sv) ); > + VERIFY( ranges::equal(*i++, "quick"sv) ); > + VERIFY( ranges::equal(*i++, "brown"sv) ); > + VERIFY( ranges::equal(*i++, "fox"sv) ); > + VERIFY( i == v.end() ); > +} > + > +void > +test04() > +{ > + auto x = "the quick brown fox"sv; > + std::initializer_list<char> p = {' ', ' '}; > + static_assert(!ranges::view<decltype(p)>); > + static_assert(std::same_as<decltype(p | views::all), > + ranges::ref_view<decltype(p)>>); > + auto v = x | views::split(views::all(p)); // views::all is needed here after P2281. > + auto i = v.begin(); > + VERIFY( ranges::equal(*i++, "the"sv) ); > + VERIFY( ranges::equal(*i++, "quick"sv) ); > + VERIFY( ranges::equal(*i++, "brown"sv) ); > + VERIFY( ranges::equal(*i++, "fox"sv) ); > + VERIFY( i == v.end() ); > +} > + > +void > +test05() > +{ > + auto as_string = [](ranges::view auto rng) { > + auto in = rng | views::common; > + return std::string(in.begin(), in.end()); > + }; > + std::string str > + = "Now is the time for all good men to come to the aid of their county."; > + auto rng > + = str | views::split(' ') | views::transform(as_string) | views::common; > + std::vector<std::string> words(rng.begin(), rng.end()); > + auto not_space_p = [](char c) { return c != ' '; }; > + VERIFY( ranges::equal(words | views::join, > + str | views::filter(not_space_p)) ); > +} > + > +template<auto split = views::split> > +void > +test06() > +{ > + // Verify SFINAE behavior. > + std::string s, p; > + static_assert(!requires { split(); }); > + static_assert(!requires { split(s, p, 0); }); > + static_assert(!requires { split(p)(); }); > + static_assert(!requires { s | split; }); > + > + static_assert(!requires { s | split(p); }); > + static_assert(!requires { split(p)(s); }); > + static_assert(!requires { s | (split(p) | views::all); }); > + static_assert(!requires { (split(p) | views::all)(s); }); > + > + static_assert(requires { s | split(views::all(p)); }); > + static_assert(requires { split(views::all(p))(s); }); > + static_assert(requires { s | (split(views::all(p)) | views::all); }); > + static_assert(requires { (split(views::all(p)) | views::all)(s); }); > + > + auto adapt = split(p); > + static_assert(requires { s | adapt; }); > + static_assert(requires { adapt(s); }); > + > + auto adapt2 = split(p) | views::all; > + static_assert(requires { s | adapt2; }); > + static_assert(requires { adapt2(s); }); > +} > + > +void > +test10() > +{ > + // LWG 3505 > + auto to_string = [] (auto r) { > + return std::string(r.begin(), ranges::next(r.begin(), r.end())); > + }; > + auto v = "xxyx"sv | views::split("xy"sv) | views::transform(to_string); > + VERIFY( ranges::equal(v, (std::string_view[]){"x", "x"}) ); > +} > + > +void > +test11() > +{ > + // LWG 3478 > + static_assert(ranges::distance(views::split("text"sv, "text"sv)) == 2); > + static_assert(ranges::distance(views::split(" text "sv, ' ')) == 3); > + static_assert(ranges::distance(views::split(" t e x t "sv, ' ')) == 6); > + static_assert(ranges::distance(views::split(" text "sv, " "sv)) == 3); > + static_assert(ranges::distance(views::split(" text "sv, " "sv)) == 4); > + static_assert(ranges::distance(views::split(" text "sv, " "sv)) == 4); > + static_assert(ranges::distance(views::split("t"sv, 't')) == 2); > + static_assert(ranges::distance(views::split("text"sv, ""sv)) == 4); > +} > +} // namespace from_lazy_split_cc > + > +int > +main() > +{ > + test01(); > + > + from_lazy_split_cc::test01(); > + from_lazy_split_cc::test02(); > + from_lazy_split_cc::test03(); > + from_lazy_split_cc::test04(); > + from_lazy_split_cc::test05(); > + from_lazy_split_cc::test06(); > + from_lazy_split_cc::test10(); > + from_lazy_split_cc::test11(); > +} > diff --git a/libstdc++-v3/testsuite/std/ranges/p2325.cc b/libstdc++-v3/testsuite/std/ranges/p2325.cc > index 4d075409026..d2ebe9af863 100644 > --- a/libstdc++-v3/testsuite/std/ranges/p2325.cc > +++ b/libstdc++-v3/testsuite/std/ranges/p2325.cc > @@ -124,6 +124,20 @@ test08() > static_assert(default_initializable<type4>); > } > > +void > +test08a() > +{ > + // Verify split_view is conditionally default constructible. > + using type1 = ranges::split_view<ranges::ref_view<int[2]>, ranges::single_view<int>>; > + static_assert(!default_initializable<type1>); > + using type2 = ranges::split_view<ranges::single_view<int>, ranges::ref_view<int[2]>>; > + static_assert(!default_initializable<type2>); > + using type3 = ranges::split_view<ranges::ref_view<int[2]>, ranges::ref_view<int[2]>>; > + static_assert(!default_initializable<type3>); > + using type4 = ranges::split_view<ranges::single_view<int>, ranges::single_view<int>>; > + static_assert(default_initializable<type4>); > +} > + > void > test09() > { > diff --git a/libstdc++-v3/testsuite/std/ranges/p2367.cc b/libstdc++-v3/testsuite/std/ranges/p2367.cc > index 5228b021602..70a0304593f 100644 > --- a/libstdc++-v3/testsuite/std/ranges/p2367.cc > +++ b/libstdc++-v3/testsuite/std/ranges/p2367.cc > @@ -45,4 +45,5 @@ test01() > > // Verify changes to views::lazy_split. > auto v6 = views::lazy_split(x, 5u); > + auto v7 = views::split(x, 5u); > } > -- > 2.32.0.93.g670b81a890 > ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 1/5] libstdc++: Implement P2325 changes to default-constructibilty of views 2021-06-17 15:22 [PATCH 1/5] libstdc++: Implement P2325 changes to default-constructibilty of views Patrick Palka ` (3 preceding siblings ...) 2021-06-17 15:22 ` [PATCH 5/5] libstdc++: Implement new views::split " Patrick Palka @ 2021-06-17 18:19 ` Jonathan Wakely 2021-06-17 18:28 ` Jonathan Wakely 4 siblings, 1 reply; 11+ messages in thread From: Jonathan Wakely @ 2021-06-17 18:19 UTC (permalink / raw) To: Patrick Palka; +Cc: gcc-patches, libstdc++ On Thu, 17 Jun 2021, 16:50 Patrick Palka via Libstdc++, < libstdc++@gcc.gnu.org> wrote: > This implements the wording changes of P2325R3 "Views should not be > required to be default constructible". Changes are relatively > straightforward, besides perhaps those to __box (which now stands > for copyable-box instead of semiregular-box) and __non_propagating_cache. > > For __box, this patch implements the recommended practice to also avoid > std::optional when the boxed type is nothrow_move/copy_constructible. > > For __non_propagating_cache, now that it's used by split_view::_M_current, > we need to add assignment from a value of the underlying type to the > subset of the std::optional API implemented for the cache (needed by > split_view::begin()). Hence the new __non_propagating_cache::operator= > overload. > > While we're changing __box, this fixes the undesirable list-init in > the constuctors of the partial specialization as reported in PR100475 > comment #7. As I said on IRC, I'm not sure why the defaulted default constructors need to be constrained (rather than just letting them get deleted) but the patch is OK. ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 1/5] libstdc++: Implement P2325 changes to default-constructibilty of views 2021-06-17 18:19 ` [PATCH 1/5] libstdc++: Implement P2325 changes to default-constructibilty of views Jonathan Wakely @ 2021-06-17 18:28 ` Jonathan Wakely 0 siblings, 0 replies; 11+ messages in thread From: Jonathan Wakely @ 2021-06-17 18:28 UTC (permalink / raw) To: Patrick Palka; +Cc: gcc-patches, libstdc++ On Thu, 17 Jun 2021, 19:19 Jonathan Wakely, <jwakely.gcc@gmail.com> wrote: > > > On Thu, 17 Jun 2021, 16:50 Patrick Palka via Libstdc++, < > libstdc++@gcc.gnu.org> wrote: > >> This implements the wording changes of P2325R3 "Views should not be >> required to be default constructible". Changes are relatively >> straightforward, besides perhaps those to __box (which now stands >> for copyable-box instead of semiregular-box) and __non_propagating_cache. >> >> For __box, this patch implements the recommended practice to also avoid >> std::optional when the boxed type is nothrow_move/copy_constructible. >> >> For __non_propagating_cache, now that it's used by split_view::_M_current, >> we need to add assignment from a value of the underlying type to the >> subset of the std::optional API implemented for the cache (needed by >> split_view::begin()). Hence the new __non_propagating_cache::operator= >> overload. >> >> While we're changing __box, this fixes the undesirable list-init in >> the constuctors of the partial specialization as reported in PR100475 >> comment #7. > > > > As I said on IRC, I'm not sure why the defaulted default constructors need > to be constrained (rather than just letting them get deleted) but the patch > is OK. > Because the default member initializers are in the immediate context, so the constructor would look valid to is_default_constructible, and only get deleted when instantiated. (Thanks to Barry for reminding me of that.) > ^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2021-06-18 21:58 UTC | newest] Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2021-06-17 15:22 [PATCH 1/5] libstdc++: Implement P2325 changes to default-constructibilty of views Patrick Palka 2021-06-17 15:22 ` [PATCH 2/5] libstdc++: Move ranges algos used by <ranges> into ranges_util.h Patrick Palka 2021-06-17 18:14 ` Jonathan Wakely 2021-06-17 15:22 ` [PATCH 3/5] libstdc++: Rename views::split to views::lazy_split as per P2210 Patrick Palka 2021-06-18 21:56 ` Jonathan Wakely 2021-06-17 15:22 ` [PATCH 4/5] libstdc++: Implement resolution of LWG 3478 " Patrick Palka 2021-06-18 21:57 ` Jonathan Wakely 2021-06-17 15:22 ` [PATCH 5/5] libstdc++: Implement new views::split " Patrick Palka 2021-06-18 21:58 ` Jonathan Wakely 2021-06-17 18:19 ` [PATCH 1/5] libstdc++: Implement P2325 changes to default-constructibilty of views Jonathan Wakely 2021-06-17 18:28 ` Jonathan Wakely
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).