Tested x86_64-linux. Pushed to trunk. -- >8 --
This is a C++23 feature that relaxes the constraints on some range adaptors, to support move-only types. libstdc++-v3/ChangeLog: * include/bits/version.def (ranges): Update value. * include/bits/version.h: Regenerate. * include/std/ranges (__detail::__boxable): Use move_constructible instead of copy_constructible for C++23. (__detail::__box<T>): Adjust constraints for partial specialization. (single_view, transform_view): Use __box_constructible instead of copy_constructible in constraints. (zip_transform_view, adjacent_transform_view, repeat_view): Use move_constructible instead of copy_constructible in constraints. * testsuite/std/ranges/adaptors/adjacent_transform/1.cc: Check construction from move-only argument. * testsuite/std/ranges/adaptors/transform.cc: Likewise. * testsuite/std/ranges/repeat/1.cc: Likewise. * testsuite/std/ranges/single_view.cc: Likewise. * testsuite/std/ranges/zip_transform/1.cc: Likewise. * testsuite/std/ranges/version_c++23.cc: Adjust expected value of __cpp_lib_ranges. --- libstdc++-v3/include/bits/version.def | 2 +- libstdc++-v3/include/bits/version.h | 4 +- libstdc++-v3/include/std/ranges | 72 +++++++++++++------ .../ranges/adaptors/adjacent_transform/1.cc | 14 ++++ .../std/ranges/adaptors/transform.cc | 19 +++++ libstdc++-v3/testsuite/std/ranges/repeat/1.cc | 12 ++++ .../testsuite/std/ranges/single_view.cc | 17 +++++ .../testsuite/std/ranges/version_c++23.cc | 2 +- .../testsuite/std/ranges/zip_transform/1.cc | 14 ++++ 9 files changed, 131 insertions(+), 25 deletions(-) diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def index 80c13d4a447..44b916e3897 100644 --- a/libstdc++-v3/include/bits/version.def +++ b/libstdc++-v3/include/bits/version.def @@ -1045,7 +1045,7 @@ ftms = { ftms = { name = ranges; values = { - v = 202202; + v = 202207; cxxmin = 23; extra_cond = "__glibcxx_concepts"; }; diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h index 5bddb4b8adc..9fada98eee3 100644 --- a/libstdc++-v3/include/bits/version.h +++ b/libstdc++-v3/include/bits/version.h @@ -1290,9 +1290,9 @@ // from version.def line 1046 #if !defined(__cpp_lib_ranges) # if (__cplusplus >= 202302L) && (__glibcxx_concepts) -# define __glibcxx_ranges 202202L +# define __glibcxx_ranges 202207L # if defined(__glibcxx_want_all) || defined(__glibcxx_want_ranges) -# define __cpp_lib_ranges 202202L +# define __cpp_lib_ranges 202207L # endif # elif (__cplusplus >= 202002L) && (__glibcxx_concepts) # define __glibcxx_ranges 202110L diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges index 3477323d871..1d529a886be 100644 --- a/libstdc++-v3/include/std/ranges +++ b/libstdc++-v3/include/std/ranges @@ -106,8 +106,14 @@ namespace ranges namespace __detail { +#if __cpp_lib_ranges >= 202207L // C++ >= 23 + // P2494R2 Relaxing range adaptors to allow for move only types + template<typename _Tp> + concept __boxable = move_constructible<_Tp> && is_object_v<_Tp>; +#else template<typename _Tp> concept __boxable = copy_constructible<_Tp> && is_object_v<_Tp>; +#endif template<__boxable _Tp> struct __box : std::optional<_Tp> @@ -132,7 +138,7 @@ namespace ranges constexpr __box& operator=(const __box& __that) noexcept(is_nothrow_copy_constructible_v<_Tp>) - requires (!copyable<_Tp>) + requires (!copyable<_Tp>) && copy_constructible<_Tp> { if (this != std::__addressof(__that)) { @@ -160,13 +166,22 @@ namespace ranges } }; - // 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<typename _Tp> + concept __boxable_copyable + = copy_constructible<_Tp> + && (copyable<_Tp> || (is_nothrow_move_constructible_v<_Tp> + && is_nothrow_copy_constructible_v<_Tp>)); + template<typename _Tp> + concept __boxable_movable + = (!copy_constructible<_Tp>) + && (movable<_Tp> || is_nothrow_move_constructible_v<_Tp>); + + // For types which are already copyable (or since C++23, movable) + // this specialization of the box 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 copyable<_Tp> || (is_nothrow_move_constructible_v<_Tp> - && is_nothrow_copy_constructible_v<_Tp>) + requires __boxable_copyable<_Tp> || __boxable_movable<_Tp> struct __box<_Tp> { private: @@ -178,6 +193,7 @@ namespace ranges constexpr explicit __box(const _Tp& __t) noexcept(is_nothrow_copy_constructible_v<_Tp>) + requires copy_constructible<_Tp> : _M_value(__t) { } @@ -198,12 +214,13 @@ namespace ranges __box(const __box&) = default; __box(__box&&) = default; __box& operator=(const __box&) requires copyable<_Tp> = default; - __box& operator=(__box&&) requires copyable<_Tp> = default; + __box& operator=(__box&&) requires movable<_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 + requires (!copyable<_Tp>) && copy_constructible<_Tp> { static_assert(is_nothrow_copy_constructible_v<_Tp>); if (this != std::__addressof(__that)) @@ -217,6 +234,7 @@ namespace ranges // Likewise for move assignment. constexpr __box& operator=(__box&& __that) noexcept + requires (!movable<_Tp>) { static_assert(is_nothrow_move_constructible_v<_Tp>); if (this != std::__addressof(__that)) @@ -250,7 +268,12 @@ namespace ranges } // namespace __detail /// A view that contains exactly one element. - template<copy_constructible _Tp> requires is_object_v<_Tp> +#if __cpp_lib_ranges >= 202207L // C++ >= 23 + template<move_constructible _Tp> +#else + template<copy_constructible _Tp> +#endif + requires is_object_v<_Tp> class single_view : public view_interface<single_view<_Tp>> { public: @@ -259,6 +282,7 @@ namespace ranges constexpr explicit single_view(const _Tp& __t) noexcept(is_nothrow_copy_constructible_v<_Tp>) + requires copy_constructible<_Tp> : _M_value(__t) { } @@ -1147,7 +1171,8 @@ namespace views::__adaptor }; } // namespace views::__adaptor -#if __cplusplus > 202002L +#if __cpp_lib_ranges >= 202202L + // P2387R3 Pipe support for user-defined range adaptors template<typename _Derived> requires is_class_v<_Derived> && same_as<_Derived, remove_cv_t<_Derived>> class range_adaptor_closure @@ -1751,7 +1776,11 @@ namespace views::__adaptor inline constexpr _Filter filter; } // namespace views +#if __cpp_lib_ranges >= 202207L // C++ >= 23 + template<input_range _Vp, move_constructible _Fp> +#else template<input_range _Vp, copy_constructible _Fp> +#endif requires view<_Vp> && is_object_v<_Fp> && regular_invocable<_Fp&, range_reference_t<_Vp>> && std::__detail::__can_reference<invoke_result_t<_Fp&, @@ -4623,7 +4652,7 @@ namespace views::__adaptor return input_iterator_tag{}; } - template<copy_constructible _Fp, input_range... _Ws> + template<move_constructible _Fp, input_range... _Ws> requires (view<_Ws> && ...) && (sizeof...(_Ws) > 0) && is_object_v<_Fp> && regular_invocable<_Fp&, range_reference_t<_Ws>...> && std::__detail::__can_reference<invoke_result_t<_Fp&, range_reference_t<_Ws>...>> @@ -4894,7 +4923,7 @@ namespace views::__adaptor = typename iterator_traits<iterator_t<__maybe_const_t<_Const, _Range>>>::iterator_category; } - template<copy_constructible _Fp, input_range... _Vs> + template<move_constructible _Fp, input_range... _Vs> requires (view<_Vs> && ...) && (sizeof...(_Vs) > 0) && is_object_v<_Fp> && regular_invocable<_Fp&, range_reference_t<_Vs>...> && std::__detail::__can_reference<invoke_result_t<_Fp&, range_reference_t<_Vs>...>> @@ -5001,7 +5030,7 @@ namespace views::__adaptor template<class _Fp, class... Rs> zip_transform_view(_Fp, Rs&&...) -> zip_transform_view<_Fp, views::all_t<Rs>...>; - template<copy_constructible _Fp, input_range... _Vs> + template<move_constructible _Fp, input_range... _Vs> requires (view<_Vs> && ...) && (sizeof...(_Vs) > 0) && is_object_v<_Fp> && regular_invocable<_Fp&, range_reference_t<_Vs>...> && std::__detail::__can_reference<invoke_result_t<_Fp&, range_reference_t<_Vs>...>> @@ -5131,7 +5160,7 @@ namespace views::__adaptor { return __x._M_inner - __y._M_inner; } }; - template<copy_constructible _Fp, input_range... _Vs> + template<move_constructible _Fp, input_range... _Vs> requires (view<_Vs> && ...) && (sizeof...(_Vs) > 0) && is_object_v<_Fp> && regular_invocable<_Fp&, range_reference_t<_Vs>...> && std::__detail::__can_reference<invoke_result_t<_Fp&, range_reference_t<_Vs>...>> @@ -5338,7 +5367,7 @@ namespace views::__adaptor friend class adjacent_view; - template<forward_range _Wp, copy_constructible _Fp, size_t _Mm> + template<forward_range _Wp, move_constructible _Fp, size_t _Mm> requires view<_Wp> && (_Mm > 0) && is_object_v<_Fp> && regular_invocable<__detail::__unarize<_Fp&, _Mm>, range_reference_t<_Wp>> && std::__detail::__can_reference<invoke_result_t<__detail::__unarize<_Fp&, _Mm>, @@ -5580,7 +5609,7 @@ namespace views::__adaptor inline constexpr auto pairwise = adjacent<2>; } - template<forward_range _Vp, copy_constructible _Fp, size_t _Nm> + template<forward_range _Vp, move_constructible _Fp, size_t _Nm> requires view<_Vp> && (_Nm > 0) && is_object_v<_Fp> && regular_invocable<__detail::__unarize<_Fp&, _Nm>, range_reference_t<_Vp>> && std::__detail::__can_reference<invoke_result_t<__detail::__unarize<_Fp&, _Nm>, @@ -5650,7 +5679,7 @@ namespace views::__adaptor { return _M_inner.size(); } }; - template<forward_range _Vp, copy_constructible _Fp, size_t _Nm> + template<forward_range _Vp, move_constructible _Fp, size_t _Nm> requires view<_Vp> && (_Nm > 0) && is_object_v<_Fp> && regular_invocable<__detail::__unarize<_Fp&, _Nm>, range_reference_t<_Vp>> && std::__detail::__can_reference<invoke_result_t<__detail::__unarize<_Fp&, _Nm>, @@ -5819,7 +5848,7 @@ namespace views::__adaptor { return __x._M_inner - __y._M_inner; } }; - template<forward_range _Vp, copy_constructible _Fp, size_t _Nm> + template<forward_range _Vp, move_constructible _Fp, size_t _Nm> requires view<_Vp> && (_Nm > 0) && is_object_v<_Fp> && regular_invocable<__detail::__unarize<_Fp&, _Nm>, range_reference_t<_Vp>> && std::__detail::__can_reference<invoke_result_t<__detail::__unarize<_Fp&, _Nm>, @@ -7528,8 +7557,8 @@ namespace views::__adaptor } // namespace views #endif // __cpp_lib_ranges_join_with -#ifdef __cpp_lib_ranges_repeat // C++ >= 32 - template<copy_constructible _Tp, semiregular _Bound = unreachable_sentinel_t> +#ifdef __cpp_lib_ranges_repeat // C++ >= 23 + template<move_constructible _Tp, semiregular _Bound = unreachable_sentinel_t> requires is_object_v<_Tp> && same_as<_Tp, remove_cv_t<_Tp>> && (__detail::__is_integer_like<_Bound> || same_as<_Bound, unreachable_sentinel_t>) class repeat_view : public view_interface<repeat_view<_Tp, _Bound>> @@ -7552,6 +7581,7 @@ namespace views::__adaptor constexpr explicit repeat_view(const _Tp& __value, _Bound __bound = _Bound()) + requires copy_constructible<_Tp> : _M_value(__value), _M_bound(__bound) { if constexpr (!same_as<_Bound, unreachable_sentinel_t>) @@ -7594,7 +7624,7 @@ namespace views::__adaptor template<typename _Tp, typename _Bound> repeat_view(_Tp, _Bound) -> repeat_view<_Tp, _Bound>; - template<copy_constructible _Tp, semiregular _Bound> + template<move_constructible _Tp, semiregular _Bound> requires is_object_v<_Tp> && same_as<_Tp, remove_cv_t<_Tp>> && (__detail::__is_integer_like<_Bound> || same_as<_Bound, unreachable_sentinel_t>) class repeat_view<_Tp, _Bound>::_Iterator diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/adjacent_transform/1.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/adjacent_transform/1.cc index 07f20b702dd..c32c639e815 100644 --- a/libstdc++-v3/testsuite/std/ranges/adaptors/adjacent_transform/1.cc +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/adjacent_transform/1.cc @@ -97,10 +97,24 @@ test03() return true; } +void +test04() +{ + extern int x[5]; + struct move_only { + move_only() { } + move_only(move_only&&) { } + int operator()(int i, int j) const { return i + j; } + }; + // P2494R2 Relaxing range adaptors to allow for move only types + static_assert( requires { views::pairwise_transform(x, move_only{}); } ); +} + int main() { static_assert(test01()); static_assert(test02()); static_assert(test03()); + test04(); } diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc index e89ae4f9f3f..9d52cb01dad 100644 --- a/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc @@ -175,6 +175,24 @@ test08() static_assert(!requires { views::all | transform; }); } +template<auto transform = views::transform> +void +test09() +{ + extern int x[5]; + struct move_only { + move_only() { } + move_only(move_only&&) { } + int operator()(int i) const { return i; } + }; +#if __cpp_lib_ranges >= 202207L + // P2494R2 Relaxing range adaptors to allow for move only types + static_assert( requires { transform(x, move_only{}); } ); +#else + static_assert( ! requires { transform(x, move_only{}); } ); +#endif +} + int main() { @@ -186,4 +204,5 @@ main() test06(); test07(); test08(); + test09(); } diff --git a/libstdc++-v3/testsuite/std/ranges/repeat/1.cc b/libstdc++-v3/testsuite/std/ranges/repeat/1.cc index 07b70891042..c62122c078f 100644 --- a/libstdc++-v3/testsuite/std/ranges/repeat/1.cc +++ b/libstdc++-v3/testsuite/std/ranges/repeat/1.cc @@ -127,6 +127,17 @@ test05() ranges::repeat_view<int> r; } +void +test06() +{ + struct move_only { + move_only() { } + move_only(move_only&&) { } + }; + // P2494R2 Relaxing range adaptors to allow for move only types + static_assert( requires { views::repeat(move_only{}, 2); } ); +} + int main() { @@ -135,4 +146,5 @@ main() static_assert(test03()); static_assert(test04()); test05(); + test06(); } diff --git a/libstdc++-v3/testsuite/std/ranges/single_view.cc b/libstdc++-v3/testsuite/std/ranges/single_view.cc index 38a3946ca43..8dfe6bb4bd1 100644 --- a/libstdc++-v3/testsuite/std/ranges/single_view.cc +++ b/libstdc++-v3/testsuite/std/ranges/single_view.cc @@ -123,6 +123,22 @@ test07() static_assert(!requires { single(uncopyable{}); }); } +template<auto single = std::views::single> +void +test08() +{ + struct move_only { + move_only() { } + move_only(move_only&&) { } + }; +#if __cpp_lib_ranges >= 202207L + // P2494R2 Relaxing range adaptors to allow for move only types + static_assert( requires { single(move_only{}); } ); +#else + static_assert( ! requires { single(move_only{}); } ); +#endif +} + int main() { test01(); @@ -132,4 +148,5 @@ int main() test05(); test06(); test07(); + test08(); } diff --git a/libstdc++-v3/testsuite/std/ranges/version_c++23.cc b/libstdc++-v3/testsuite/std/ranges/version_c++23.cc index e8342fa986a..8e4a8b466aa 100644 --- a/libstdc++-v3/testsuite/std/ranges/version_c++23.cc +++ b/libstdc++-v3/testsuite/std/ranges/version_c++23.cc @@ -4,7 +4,7 @@ #include <version> #if __STDC_HOSTED__ -# if __cpp_lib_ranges != 202202L +# if __cpp_lib_ranges != 202207L # error "Feature-test macro __cpp_lib_ranges has wrong value in <version>" # endif #endif diff --git a/libstdc++-v3/testsuite/std/ranges/zip_transform/1.cc b/libstdc++-v3/testsuite/std/ranges/zip_transform/1.cc index 5ea24c578a8..d858b02f199 100644 --- a/libstdc++-v3/testsuite/std/ranges/zip_transform/1.cc +++ b/libstdc++-v3/testsuite/std/ranges/zip_transform/1.cc @@ -99,10 +99,24 @@ test03() return true; } +void +test04() +{ + extern int x[5]; + struct move_only { + move_only() { } + move_only(move_only&&) { } + int operator()(int i, int j) const { return i + j; } + }; + // P2494R2 Relaxing range adaptors to allow for move only types + static_assert( requires { views::zip_transform(move_only{}, x, x); } ); +} + int main() { static_assert(test01()); static_assert(test02()); static_assert(test03()); + test04(); } -- 2.41.0