On Thu, 23 Nov 2023, Jonathan Wakely wrote: > Here's the finished version of the std::ranges::to patch, which I've > pushed to trunk. > > Tested x86_64-linux. > > -- >8 -- > > This adds the std::ranges::to functions for C++23. The rest of P1206R7 > is not yet implemented, i.e. the new constructors taking the > std::from_range tag, and the new insert_range, assign_range, etc. member > functions. std::ranges::to works with the standard containers even > without the new constructors, so this is useful immediately. > > The __cpp_lib_ranges_to_container feature test macro can be defined now, > because that only indicates support for the changes in <ranges>, which > are implemented by this patch. The __cpp_lib_containers_ranges macro > will be defined once all containers support the new member functions. > > libstdc++-v3/ChangeLog: > > PR libstdc++/111055 > * include/bits/ranges_base.h (from_range_t): Define new tag > type. > (from_range): Define new tag object. > * include/bits/version.def (ranges_to_container): Define. > * include/bits/version.h: Regenerate. > * include/std/ranges (ranges::to): Define. > * testsuite/std/ranges/conv/1.cc: New test. > * testsuite/std/ranges/conv/2_neg.cc: New test. > * testsuite/std/ranges/conv/version.cc: New test. > --- > libstdc++-v3/include/bits/ranges_base.h | 8 +- > libstdc++-v3/include/bits/version.def | 34 +- > libstdc++-v3/include/bits/version.h | 111 +++--- > libstdc++-v3/include/std/ranges | 361 ++++++++++++++++- > libstdc++-v3/testsuite/std/ranges/conv/1.cc | 369 ++++++++++++++++++ > .../testsuite/std/ranges/conv/2_neg.cc | 24 ++ > .../testsuite/std/ranges/conv/version.cc | 19 + > 7 files changed, 866 insertions(+), 60 deletions(-) > create mode 100644 libstdc++-v3/testsuite/std/ranges/conv/1.cc > create mode 100644 libstdc++-v3/testsuite/std/ranges/conv/2_neg.cc > create mode 100644 libstdc++-v3/testsuite/std/ranges/conv/version.cc > > diff --git a/libstdc++-v3/include/bits/ranges_base.h > b/libstdc++-v3/include/bits/ranges_base.h > index 7fa43d1965a..1ca2c5ce2bb 100644 > --- a/libstdc++-v3/include/bits/ranges_base.h > +++ b/libstdc++-v3/include/bits/ranges_base.h > @@ -37,6 +37,7 @@ > #include <bits/stl_iterator.h> > #include <ext/numeric_traits.h> > #include <bits/max_size_type.h> > +#include <bits/version.h> > > #ifdef __cpp_lib_concepts > namespace std _GLIBCXX_VISIBILITY(default) > @@ -1056,8 +1057,13 @@ namespace ranges > using borrowed_iterator_t = __conditional_t<borrowed_range<_Range>, > iterator_t<_Range>, > dangling>; > - > } // namespace ranges > + > +#if __glibcxx_ranges_to_container // C++ >= 23 > + struct from_range_t { explicit from_range_t() = default; }; > + inline constexpr from_range_t from_range{}; > +#endif > + > _GLIBCXX_END_NAMESPACE_VERSION > } // namespace std > #endif // library concepts > diff --git a/libstdc++-v3/include/bits/version.def > b/libstdc++-v3/include/bits/version.def > index 605708dfee7..140777832ed 100644 > --- a/libstdc++-v3/include/bits/version.def > +++ b/libstdc++-v3/include/bits/version.def > @@ -1439,19 +1439,21 @@ ftms = { > }; > }; > > -ftms = { > - name = to_underlying; > - values = { > - v = 202102; > - cxxmin = 23; > - }; > -}; > +//ftms = { > +// name = container_ranges; > +// values = { > +// v = 202202; > +// cxxmin = 23; > +// hosted = yes; > +// }; > +//}; > > ftms = { > - name = unreachable; > + name = ranges_to_container; > values = { > v = 202202; > cxxmin = 23; > + hosted = yes; > }; > }; > > @@ -1683,6 +1685,22 @@ ftms = { > }; > }; > > +ftms = { > + name = to_underlying; > + values = { > + v = 202102; > + cxxmin = 23; > + }; > +}; > + > +ftms = { > + name = unreachable; > + values = { > + v = 202202; > + cxxmin = 23; > + }; > +}; > + > ftms = { > name = fstream_native_handle; > values = { > diff --git a/libstdc++-v3/include/bits/version.h > b/libstdc++-v3/include/bits/version.h > index cacd9375cab..1fb1d148459 100644 > --- a/libstdc++-v3/include/bits/version.h > +++ b/libstdc++-v3/include/bits/version.h > @@ -1740,29 +1740,18 @@ > #endif /* !defined(__cpp_lib_reference_from_temporary) && > defined(__glibcxx_want_reference_from_temporary) */ > #undef __glibcxx_want_reference_from_temporary > > -// from version.def line 1443 > -#if !defined(__cpp_lib_to_underlying) > -# if (__cplusplus >= 202100L) > -# define __glibcxx_to_underlying 202102L > -# if defined(__glibcxx_want_all) || defined(__glibcxx_want_to_underlying) > -# define __cpp_lib_to_underlying 202102L > +// from version.def line 1452 > +#if !defined(__cpp_lib_ranges_to_container) > +# if (__cplusplus >= 202100L) && _GLIBCXX_HOSTED > +# define __glibcxx_ranges_to_container 202202L > +# if defined(__glibcxx_want_all) || > defined(__glibcxx_want_ranges_to_container) > +# define __cpp_lib_ranges_to_container 202202L > # endif > # endif > -#endif /* !defined(__cpp_lib_to_underlying) && > defined(__glibcxx_want_to_underlying) */ > -#undef __glibcxx_want_to_underlying > +#endif /* !defined(__cpp_lib_ranges_to_container) && > defined(__glibcxx_want_ranges_to_container) */ > +#undef __glibcxx_want_ranges_to_container > > -// from version.def line 1451 > -#if !defined(__cpp_lib_unreachable) > -# if (__cplusplus >= 202100L) > -# define __glibcxx_unreachable 202202L > -# if defined(__glibcxx_want_all) || defined(__glibcxx_want_unreachable) > -# define __cpp_lib_unreachable 202202L > -# endif > -# endif > -#endif /* !defined(__cpp_lib_unreachable) && > defined(__glibcxx_want_unreachable) */ > -#undef __glibcxx_want_unreachable > - > -// from version.def line 1459 > +// from version.def line 1461 > #if !defined(__cpp_lib_ranges_zip) > # if (__cplusplus >= 202100L) > # define __glibcxx_ranges_zip 202110L > @@ -2059,7 +2048,29 @@ > #endif /* !defined(__cpp_lib_string_resize_and_overwrite) && > defined(__glibcxx_want_string_resize_and_overwrite) */ > #undef __glibcxx_want_string_resize_and_overwrite > > -// from version.def line 1687 > +// from version.def line 1689 > +#if !defined(__cpp_lib_to_underlying) > +# if (__cplusplus >= 202100L) > +# define __glibcxx_to_underlying 202102L > +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_to_underlying) > +# define __cpp_lib_to_underlying 202102L > +# endif > +# endif > +#endif /* !defined(__cpp_lib_to_underlying) && > defined(__glibcxx_want_to_underlying) */ > +#undef __glibcxx_want_to_underlying > + > +// from version.def line 1697 > +#if !defined(__cpp_lib_unreachable) > +# if (__cplusplus >= 202100L) > +# define __glibcxx_unreachable 202202L > +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_unreachable) > +# define __cpp_lib_unreachable 202202L > +# endif > +# endif > +#endif /* !defined(__cpp_lib_unreachable) && > defined(__glibcxx_want_unreachable) */ > +#undef __glibcxx_want_unreachable > + > +// from version.def line 1705 > #if !defined(__cpp_lib_fstream_native_handle) > # if (__cplusplus > 202302L) && _GLIBCXX_HOSTED > # define __glibcxx_fstream_native_handle 202306L > diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges > index 26d6c013ad0..63bea862c05 100644 > --- a/libstdc++-v3/include/std/ranges > +++ b/libstdc++-v3/include/std/ranges > @@ -64,6 +64,7 @@ > #define __glibcxx_want_ranges_repeat > #define __glibcxx_want_ranges_slide > #define __glibcxx_want_ranges_stride > +#define __glibcxx_want_ranges_to_container > #define __glibcxx_want_ranges_zip > #include <bits/version.h> > > @@ -9213,8 +9214,366 @@ namespace views::__adaptor > > namespace views = ranges::views; > > +#if __cpp_lib_ranges_to_container // C++ >= 23 > +namespace ranges > +{ > +/// @cond undocumented > +namespace __detail > +{ > + template<typename _Container> > + constexpr bool __reservable_container > + = sized_range<_Container> > + && requires(_Container& __c, range_size_t<_Container> __n) { > + __c.reserve(__n); > + { __c.capacity() } -> same_as<decltype(__n)>; > + { __c.max_size() } -> same_as<decltype(__n)>; > + }; > + > + template<typename _Container, typename _Ref> > + constexpr bool __container_insertable > + = requires(_Container& __c, _Ref&& __ref) { > + typename _Container::value_type; > + requires ( > + requires { __c.push_back(std::forward<_Ref>(__ref)); } > + || requires { __c.insert(__c.end(), std::forward<_Ref>(__ref)); } > + ); > + }; > + > + template<typename _Ref, typename _Container> > + constexpr auto > + __container_inserter(_Container& __c) > + { > + if constexpr (requires { __c.push_back(std::declval<_Ref>()); }) > + return std::back_inserter(__c); > + else > + return std::inserter(__c, __c.end()); > + } > + > + template<typename _Cont, typename _Range> > + constexpr bool __toable = requires { > + requires (!input_range<_Range>
It seems this sholud be input_range<_Cont>? > + || convertible_to<range_reference_t<_Range>, > + range_value_t<_Cont>>); > + }; > +} // namespace __detail > +/// @endcond > + > + /// Convert a range to a container. > + /** > + * @tparam _Cont A container type. > + * @param __r A range that models the `input_range` concept. > + * @param __args... Arguments to pass to the container constructor. > + * @since C++23 > + * > + * This function converts a range to the `_Cont` type. > + * > + * For example, `std::ranges::to<std::vector<int>>(some_view)` > + * will convert the view to `std::vector<int>`. > + * > + * Additional constructor arguments for the container can be supplied after > + * the input range argument, e.g. > + * `std::ranges::to<std::vector<int, Alloc<int>>>(a_range, an_allocator)`. > + */ > + template<typename _Cont, input_range _Rg, typename... _Args> > + requires (!view<_Cont>) > + constexpr _Cont > + to [[nodiscard]] (_Rg&& __r, _Args&&... __args) > + { > + static_assert(!is_const_v<_Cont> && !is_volatile_v<_Cont>); > + static_assert(is_class_v<_Cont>); > + > + if constexpr (__detail::__toable<_Cont, _Rg>) > + { > + if constexpr (constructible_from<_Cont, _Rg, _Args...>) > + return _Cont(std::forward<_Rg>(__r), > + std::forward<_Args>(__args)...); > + else if constexpr (constructible_from<_Cont, from_range_t, _Rg, > _Args...>) > + return _Cont(from_range, std::forward<_Rg>(__r), > + std::forward<_Args>(__args)...); > + else if constexpr (requires { common_range<_Rg>; Missing 'requires' before common_range? > + typename __iter_category_t<iterator_t<_Rg>>; > + requires derived_from<__iter_category_t<iterator_t<_Rg>>, > + input_iterator_tag>; > + requires constructible_from<_Cont, iterator_t<_Rg>, > + sentinel_t<_Rg>, _Args...>; > + }) > + return _Cont(ranges::begin(__r), ranges::end(__r), > + std::forward<_Args>(__args)...); > + else > + { > + using __detail::__container_insertable; > + using __detail::__reservable_container; > + using _RefT = range_reference_t<_Rg>; > + static_assert(constructible_from<_Cont, _Args...>); > + static_assert(__container_insertable<_Cont, _RefT>); > + _Cont __c(std::forward<_Args>(__args)...); > + if constexpr (sized_range<_Rg> && __reservable_container<_Cont>) > + > __c.reserve(static_cast<range_size_t<_Cont>>(ranges::size(__r))); > + auto __ins = __detail::__container_inserter<_RefT>(__c); > + for (auto&& __e : __r) > + *__ins++ = std::forward<decltype(__e)>(__e); > + return __c; > + } > + } > + else > + { > + static_assert(input_range<range_reference_t<_Rg>>); > + // _GLIBCXX_RESOLVE_LIB_DEFECTS > + // 3984. ranges::to's recursion branch may be ill-formed > + return ranges::to<_Cont>(ref_view(__r) | views::transform( > + []<typename _Elt>(_Elt&& __elem) { > + using _ValT = range_value_t<_Cont>; > + return ranges::to<_ValT>(std::forward<_Elt>(__elem)); > + }), std::forward<_Args>(__args)...); > + } > + } > + > +/// @cond undocumented > +namespace __detail > +{ > + template<typename _Rg> > + struct _InputIter > + { > + using iterator_category = input_iterator_tag; > + using value_type = range_value_t<_Rg>; > + using difference_type = ptrdiff_t; > + using pointer = add_pointer_t<range_reference_t<_Rg>>; > + using reference = range_reference_t<_Rg>; > + reference operator*() const; > + pointer operator->() const; > + _InputIter& operator++(); > + _InputIter operator++(int); > + bool operator==(const _InputIter&) const; > + }; > + > +#if 0 > + template<template<typename...> typename _Cont, typename _Rg, > + typename... _Args> > + concept __deduce_expr_1 = requires { > + _Cont(std::declval<_Rg>(), std::declval<_Args>()...); > + }; > + > + template<template<typename...> typename _Cont, typename _Rg, > + typename... _Args> > + concept __deduce_expr_2 = requires { > + _Cont(from_range, std::declval<_Rg>(), std::declval<_Args>()...); > + }; > + > + template<template<typename...> typename _Cont, typename _Rg, > + typename... _Args> > + concept __deduce_expr_3 = requires(_InputIter<_Rg> __i) { > + _Cont(std::move(__i), std::move(__i), std::declval<_Args>()...); > + }; > +#endif > + > + template<template<typename...> typename _Cont, input_range _Rg, > + typename... _Args> > + using _DeduceExpr1 > + = decltype(_Cont(std::declval<_Rg>(), std::declval<_Args>()...)); > + > + template<template<typename...> typename _Cont, input_range _Rg, > + typename... _Args> > + using _DeduceExpr2 > + = decltype(_Cont(from_range, std::declval<_Rg>(), > + std::declval<_Args>()...)); > + > + template<template<typename...> typename _Cont, input_range _Rg, > + typename... _Args> > + using _DeduceExpr3 > + = decltype(_Cont(std::declval<_InputIter<_Rg>>(), > + std::declval<_InputIter<_Rg>>(), > + std::declval<_Args>()...)); > + > +} // namespace __detail > +/// @endcond > + > + template<template<typename...> typename _Cont, input_range _Rg, > + typename... _Args> > + constexpr auto > + to [[nodiscard]] (_Rg&& __r, _Args&&... __args) > + { > + using __detail::_DeduceExpr1; > + using __detail::_DeduceExpr2; > + using __detail::_DeduceExpr3; > + if constexpr (requires { typename _DeduceExpr1<_Cont, _Rg, _Args...>; > }) > + return ranges::to<_DeduceExpr1<_Cont, _Rg, _Args...>>( > + std::forward<_Rg>(__r), std::forward<_Args>(__args)...); > + else if constexpr (requires { typename _DeduceExpr2<_Cont, _Rg, > _Args...>; }) > + return ranges::to<_DeduceExpr2<_Cont, _Rg, _Args...>>( > + std::forward<_Rg>(__r), std::forward<_Args>(__args)...); > + else if constexpr (requires { typename _DeduceExpr3<_Cont, _Rg, > _Args...>; }) > + return ranges::to<_DeduceExpr3<_Cont, _Rg, _Args...>>( > + std::forward<_Rg>(__r), std::forward<_Args>(__args)...); > + else > + static_assert(false); // Cannot deduce container specialization. > + } > + > +/// @cond undocumented > +namespace __detail > +{ > + template<typename _Cont, typename... _Args> > + class _ToClosure > + : public views::__adaptor::_RangeAdaptorClosure<_ToClosure<_Cont, > _Args...>> > + { > + tuple<decay_t<_Args>...> _M_bound_args; > + > + public: > + _ToClosure(_Args&&... __args) Missing constexpr? > + : _M_bound_args(std::forward<_Args>(__args)...) > + { } > + > + // TODO: use explicit object functions ("deducing this"). > + > + template<typename _Rg> > + constexpr auto > + operator()(_Rg&& __r) & > + { > + return std::apply([&__r]<typename... _Tp>(_Tp&&... __args) { > + return ranges::to<_Cont>(std::forward<_Rg>(__r), > + std::forward<_Tp>(__args)...); > + }, _M_bound_args); > + } > + > + template<typename _Rg> > + constexpr auto > + operator()(_Rg&& __r) const & > + { > + return std::apply([&__r]<typename... _Tp>(_Tp&&... __args) { > + return ranges::to<_Cont>(std::forward<_Rg>(__r), > + std::forward<_Tp>(__args)...); > + }, _M_bound_args); > + } > + > + template<typename _Rg> > + constexpr auto > + operator()(_Rg&& __r) && > + { > + return std::apply([&__r]<typename... _Tp>(_Tp&&... __args) { > + return ranges::to<_Cont>(std::forward<_Rg>(__r), > + std::forward<_Tp>(__args)...); > + }, std::move(_M_bound_args)); > + } > + > + template<typename _Rg> > + constexpr auto > + operator()(_Rg&& __r) const && > + { > + return std::apply([&__r]<typename... _Tp>(_Tp&&... __args) { > + return ranges::to<_Cont>(std::forward<_Rg>(__r), > + std::forward<_Tp>(__args)...); > + }, std::move(_M_bound_args)); > + } > + }; > +} // namespace __detail > +/// @endcond > + > + /// ranges::to adaptor for converting a range to a container type > + /** > + * @tparam _Cont A container type. > + * @param __args... Arguments to pass to the container constructor. > + * @since C++23 > + * > + * This range adaptor returns a range adaptor closure object that converts > + * a range to the `_Cont` type. > + * > + * For example, `some_view | std::ranges::to<std::vector<int>>()` > + * will convert the view to `std::vector<int>`. > + * > + * Additional constructor arguments for the container can be supplied, e.g. > + * `r | std::ranges::to<std::vector<int, Alloc<int>>>(an_allocator)`. > + */ > + template<typename _Cont, typename... _Args> > + requires (!view<_Cont>) > + constexpr __detail::_ToClosure<_Cont, _Args...> > + to [[nodiscard]] (_Args&&... __args) > + { return {std::forward<_Args>(__args)...}; } > + > +/// @cond undocumented > +namespace __detail > +{ > + template<template<typename...> typename _Cont, typename... _Args> > + class _ToClosure2 > + : public views::__adaptor::_RangeAdaptorClosure<_ToClosure2<_Cont, > _Args...>> > + { > + tuple<decay_t<_Args>...> _M_bound_args; > + > + public: > + _ToClosure2(_Args&&... __args) Same here. Would it be possible to use _RangeAdaptor instead of _RangeAdaptorClosure to leverage the existing partial application/forwarding code? IIUC we'd need to allow omitting the _S_arity member to mean accept any number of arguments. I can work on that if anything. > + : _M_bound_args(std::forward<_Args>(__args)...) > + { } > + > + // TODO: use explicit object functions ("deducing this"). > + > + template<typename _Rg> > + constexpr auto > + operator()(_Rg&& __r) & > + { > + return std::apply([&__r]<typename... _Tp>(_Tp&&... __args) { > + return ranges::to<_Cont>(std::forward<_Rg>(__r), > + std::forward<_Tp>(__args)...); > + }, _M_bound_args); > + } > + > + template<typename _Rg> > + constexpr auto > + operator()(_Rg&& __r) const & > + { > + return std::apply([&__r]<typename... _Tp>(_Tp&&... __args) { > + return ranges::to<_Cont>(std::forward<_Rg>(__r), > + std::forward<_Tp>(__args)...); > + }, _M_bound_args); > + } > + > + template<typename _Rg> > + constexpr auto > + operator()(_Rg&& __r) && > + { > + return std::apply([&__r]<typename... _Tp>(_Tp&&... __args) { > + return ranges::to<_Cont>(std::forward<_Rg>(__r), > + std::forward<_Tp>(__args)...); > + }, std::move(_M_bound_args)); > + } > + > + template<typename _Rg> > + constexpr auto > + operator()(_Rg&& __r) const && > + { > + return std::apply([&__r]<typename... _Tp>(_Tp&&... __args) { > + return ranges::to<_Cont>(std::forward<_Rg>(__r), > + std::forward<_Tp>(__args)...); > + }, std::move(_M_bound_args)); > + } > + }; > +} // namespace __detail > +/// @endcond > + > + /// ranges::to adaptor for converting a range to a deduced container type. > + /** > + * @tparam _Cont A container template. > + * @param __args... Arguments to pass to the container constructor. > + * @since C++23 > + * > + * This range adaptor returns a range adaptor closure object that converts > + * a range to a specialization of the `_Cont` class template. The specific > + * specialization of `_Cont` to be used is deduced automatically. > + * > + * For example, `some_view | std::ranges::to<std::vector>(Alloc<int>{})` > + * will convert the view to `std::vector<T, Alloc<T>>`, where `T` is the > + * view's value type, i.e. > `std::ranges::range_value_t<decltype(some_view)>`. > + * > + * Additional constructor arguments for the container can be supplied, e.g. > + * `r | std::ranges::to<std::vector>(an_allocator)`. > + */ > + template<template<typename...> typename _Cont, typename... _Args> > + constexpr __detail::_ToClosure2<_Cont, _Args...> > + to [[nodiscard]] (_Args&&... __args) > + { return {std::forward<_Args>(__args)...}; } > + > +} // namespace ranges > +#endif // __cpp_lib_ranges_to_container > + > _GLIBCXX_END_NAMESPACE_VERSION > -} // namespace > +} // namespace std > #endif // library concepts > #endif // C++2a > #endif /* _GLIBCXX_RANGES */ > diff --git a/libstdc++-v3/testsuite/std/ranges/conv/1.cc > b/libstdc++-v3/testsuite/std/ranges/conv/1.cc > new file mode 100644 > index 00000000000..0032cf32688 > --- /dev/null > +++ b/libstdc++-v3/testsuite/std/ranges/conv/1.cc > @@ -0,0 +1,369 @@ > +// { dg-do run { target c++23 } } > + > +// C++23 26.5.7 Range conversions [range.utility.conv] > + > +#include <ranges> > +#include <vector> > +#include <string> > +#include <deque> > +#include <list> > +#include <forward_list> > +#include <map> > +#include <testsuite_hooks.h> > +#include <testsuite_allocator.h> > +#include <testsuite_iterators.h> > + > +void > +test_p1206r7_examples() > +{ > + using Alloc = __gnu_test::uneq_allocator<int>; > + const Alloc alloc(303); > + const std::map<int, const char*> m{{1, "one"}, {2, "two"}, {3, "three"}}; > + namespace ranges = std::ranges; > + > + auto l = std::views::iota(1, 10); > + // create a vector with the elements of l > + auto vec = ranges::to<std::vector<int>>(l); // or vector{std::from_range, > l}; > + //Specify an allocator > + auto b = ranges::to<std::vector<int, Alloc>>(l, alloc); // or > vector{std::from_range, l, alloc}; > + //deducing value_type > + auto c = ranges::to<std::vector>(l); > + // explicit conversion int -> long > + auto d = ranges::to<std::vector<long>>(l); > + //Supports converting associative container to sequence containers > + auto f = ranges::to<std::vector>(m); > + //Supports converting sequence containers to associative ones > + auto g = ranges::to<std::map>(f); > + //Pipe syntax > + auto g2 = l | ranges::views::take(42) | ranges::to<std::vector>(); > + //Pipe syntax with allocator > + auto h = l | ranges::views::take(42) | ranges::to<std::vector>(alloc); > + //The pipe syntax also support specifying the type and conversions > + auto i = l | ranges::views::take(42) | ranges::to<std::vector<long>>(); > + // Nested ranges > + std::list<std::forward_list<int>> lst = {{0, 1, 2, 3}, {4, 5, 6, 7}}; > + auto vec1 = ranges::to<std::vector<std::vector<int>>>(lst); > + auto vec2 = ranges::to<std::vector<std::deque<double>>>(lst); > + > + VERIFY( vec == std::vector<int>(std::ranges::begin(l), > std::ranges::end(l)) ); > + static_assert(std::is_same_v<decltype(b), std::vector<int, Alloc>>); > + VERIFY( b == (std::vector<int, Alloc>(vec.begin(), vec.end())) ); > + VERIFY( b.get_allocator() == alloc ); > + static_assert(std::is_same_v<decltype(c), std::vector<int>>); > + VERIFY( c == vec ); > + static_assert(std::is_same_v<decltype(d), std::vector<long>>); > + VERIFY( d == std::vector<long>(vec.begin(), vec.end()) ); > + VERIFY( g == m ); > + static_assert(std::is_same_v<decltype(g2), std::vector<int>>); > + VERIFY( g2 == vec ); > + static_assert(std::is_same_v<decltype(h), std::vector<int, Alloc>>); > + VERIFY( h == b ); > + VERIFY( h.get_allocator() == alloc ); > + VERIFY( i == d ); > + static_assert(std::is_same_v<decltype(vec1), > std::vector<std::vector<int>>>); > + VERIFY( vec1[1][1] == 5 ); > + static_assert(std::is_same_v<decltype(vec2), > std::vector<std::deque<double>>>); > + VERIFY( vec2[1][2] == 6.0 ); > +} > + > +void > +test_example_1() > +{ > + using namespace std; > + using ranges::to; > + > + // Example 1 from C++23 [range.utility.conv.general] > + string_view str = "the quick brown fox"; > + auto words = views::split(str, ' ') | to<vector<string>>(); > + > + VERIFY( (is_same_v<decltype(words), vector<string>>) ); > + VERIFY( words == vector<string>({"the", "quick", "brown", "fox"}) ); > +} > + > +template<typename C> > +struct Cont1 > +{ > + template<typename R, typename... Args> > + requires std::constructible_from<C, R&, Args&...> > + Cont1(R&& r, Args&&... args) > + : c(r, args...) > + { } > + > + typename C::iterator begin(); > + typename C::iterator end(); > + > + C c; > +}; > + > +void > +test_2_1_1() > +{ > + // (2.1.1) constructible_from<C, R, Args...> > + > + std::vector<int> v{1, 2, 3, 4}; > + auto v2 = std::ranges::to<std::vector<int>>(v); > + static_assert(std::is_same_v<decltype(v2), decltype(v)>); > + VERIFY( v2 == v ); > + > + std::initializer_list<int> il{5, 6, 7, 8}; > + v2 = std::ranges::to<std::vector<int>>(il); > + VERIFY( v2 == std::vector<int>(il) ); > + > + v2 = std::ranges::to<std::vector<int>>(il, std::allocator<int>{}); > + VERIFY( v2 == std::vector<int>(il) ); > + > + using Alloc = __gnu_test::uneq_allocator<int>; > + using V = std::vector<int, Alloc>; > + > + V v3({10, 11, 12, 13}, Alloc(14)); > + auto v4 = std::ranges::to<V>(v3); > + static_assert(std::is_same_v<decltype(v4), V>); > + VERIFY( v4 == v3 ); > + VERIFY( v4.get_allocator() == v3.get_allocator() ); > + > + auto v5 = std::ranges::to<V>(v3, Alloc(33)); > + VERIFY( v5 == v3 ); > + VERIFY( v5.get_allocator() == Alloc(33) ); > + > + auto v6 = std::ranges::to<V>(il, Alloc(44)); > + VERIFY( v6 == V(il) ); > + VERIFY( v6.get_allocator() == Alloc(44) ); > + > + auto c = std::ranges::to<Cont1<V>>(V{1, 2, 3}); > + static_assert(std::is_same_v<decltype(c), Cont1<V>>); > + VERIFY( c.c == V({1, 2, 3}) ); > + > + auto c2 = std::ranges::to<Cont1<V>>(V{4, 5, 6}, Alloc(55)); > + static_assert(std::is_same_v<decltype(c2), Cont1<V>>); > + VERIFY( c2.c == V({4, 5, 6}) ); > + VERIFY( c2.c.get_allocator() == Alloc(55) ); > + > + auto c3 = std::ranges::to<Cont1<V>>(il, Alloc(66)); > + static_assert(std::is_same_v<decltype(c2), Cont1<V>>); > + VERIFY( c3.c == V(v2.begin(), v2.end()) ); > + VERIFY( c3.c.get_allocator() == Alloc(66) ); > +} > + > +template<typename C> > +struct Cont2 > +{ > + template<typename R, typename... Args> > + requires std::constructible_from<C, R&, Args&...> > + Cont2(std::from_range_t, R&& r, Args&&... args) > + : c(r, args...) > + { } > + > + typename C::iterator begin(); > + typename C::iterator end(); > + > + C c; > +}; > + > +void > +test_2_1_2() > +{ > + // (2.1.2) constructible_from<C, from_range_t, R, Args...> > + > + using Alloc = __gnu_test::uneq_allocator<int>; > + using V = std::vector<int, Alloc>; > + auto c = std::ranges::to<Cont2<V>>(V{1, 2, 3}); > + static_assert(std::is_same_v<decltype(c), Cont2<V>>); > + VERIFY( c.c == V({1, 2, 3}) ); > + > + auto c2 = std::ranges::to<Cont2<V>>(V{4, 5, 6}, Alloc(7)); > + static_assert(std::is_same_v<decltype(c2), Cont2<V>>); > + VERIFY( c2.c == V({4, 5, 6}) ); > + VERIFY( c2.c.get_allocator() == Alloc(7) ); > +} > + > +template<typename C> > +struct Cont3 > +{ > + template<typename It, typename Sent, typename... Args> > + requires std::same_as<It, Sent> > + && std::constructible_from<C, It&, Sent&, Args&...> > + Cont3(It first, Sent last, Args&&... args) > + : c(first, last, args...) > + { } > + > + typename C::iterator begin(); > + typename C::iterator end(); > + > + C c; > +}; > + > +void > +test_2_1_3() > +{ > + // (2.1.3) constructible_from<C, iterator_t<R>, sentinel_t<R<, Args...> > + > + using Alloc = __gnu_test::uneq_allocator<int>; > + using V = std::vector<int, Alloc>; > + > + std::list<unsigned> l{1u, 2u, 3u}; > + auto c = std::ranges::to<Cont3<V>>(l); > + static_assert(std::is_same_v<decltype(c), Cont3<V>>); > + VERIFY( c.c == V(l.begin(), l.end()) ); > + > + std::list<long> l2{4l, 5l, 6l}; > + auto c2 = std::ranges::to<Cont3<V>>(l2, Alloc(78)); > + static_assert(std::is_same_v<decltype(c2), Cont3<V>>); > + VERIFY( c2.c == V(l2.begin(), l2.end()) ); > + VERIFY( c2.c.get_allocator() == Alloc(78) ); > +} > + > +template<typename C, bool UsePushBack = true> > +struct Cont4 > +{ > + using value_type = typename C::value_type; > + > + // Only support construction with no args or an allocator. > + // This forces the use of either push_back or insert to fill the container. > + Cont4() { } > + Cont4(typename C::allocator_type a) : c(a) { } > + > + // Required to satisfy range > + typename C::iterator begin() { return c.begin(); } > + typename C::iterator end() { return c.end(); } > + > + // Satisfying container-insertable requires either this ... > + template<typename T> > + requires UsePushBack > + && requires(C& c, T&& t) { c.push_back(std::forward<T>(t)); } > + void > + push_back(T&& t) > + { > + c.push_back(std::forward<T>(t)); > + used_push_back = true; > + } > + > + // ... or this: > + template<typename T> > + typename C::iterator > + insert(typename C::iterator, T&& t) > + { > + used_push_back = false; > + return c.insert(c.end(), std::forward<T>(t)); > + } > + > + // Required to satisfy reservable-container > + void > + reserve(typename C::size_type n) requires requires(C& c) { c.reserve(n); } > + { > + c.reserve(n); > + used_reserve = true; > + } > + > + // Required to satisfy reservable-container > + auto size() const { return c.size(); } > + > + // Required to satisfy reservable-container > + auto capacity() const requires requires(C& c) { c.capacity(); } > + { return c.capacity(); } > + > + // Required to satisfy reservable-container > + auto max_size() const { return c.max_size(); } > + > + C c; > + bool used_push_back = false; > + bool used_reserve = false; > +}; > + > +void > +test_2_1_4() > +{ > + // (2.1.4) constructible_from<C, Args...> and > + // container-insertable<C, range_reference_t<R>> > + > + using Alloc = __gnu_test::uneq_allocator<int>; > + using V = std::vector<int, Alloc>; > + > + std::list<unsigned> l{1u, 2u, 3u}; > + auto c = std::ranges::to<Cont4<V>>(l); > + static_assert(std::is_same_v<decltype(c), Cont4<V>>); > + VERIFY( c.c == V(l.begin(), l.end()) ); > + VERIFY( c.used_push_back ); > + VERIFY( c.used_reserve ); > + > + std::list<long> l2{4l, 5l, 6l}; > + auto c2 = std::ranges::to<Cont4<V>>(l2, Alloc(78)); > + static_assert(std::is_same_v<decltype(c2), Cont4<V>>); > + VERIFY( c2.c == V(l2.begin(), l2.end()) ); > + VERIFY( c2.c.get_allocator() == Alloc(78) ); > + VERIFY( c2.used_push_back ); > + VERIFY( c2.used_reserve ); > + > + using Alloc2 = __gnu_test::uneq_allocator<short>; > + using List = std::list<short, Alloc2>; > + auto c3 = std::ranges::to<Cont4<List>>(c.c, Alloc2(99)); > + static_assert(std::is_same_v<decltype(c3), Cont4<List>>); > + VERIFY( c3.c == List(l.begin(), l.end()) ); > + VERIFY( c3.c.get_allocator() == Alloc(99) ); > + VERIFY( c3.used_push_back ); > + VERIFY( ! c3.used_reserve ); > + > + auto c4 = std::ranges::to<Cont4<List, false>>(c.c, Alloc2(111)); > + static_assert(std::is_same_v<decltype(c4), Cont4<List, false>>); > + VERIFY( c4.c == List(l.begin(), l.end()) ); > + VERIFY( c4.c.get_allocator() == Alloc(111) ); > + VERIFY( ! c4.used_push_back ); > + VERIFY( ! c4.used_reserve ); > +} > + > +void > +test_2_2() > +{ > + // (2.2) input_range<range_reference_t<R>> > + > + std::string s1[]{ "one", "two", "three", "four" }; > + std::string s2[]{ "V", "VI", "VII", "VIII" }; > + std::string s3[]{ "0x09", "0x0a", "0x0b", "0x0c" }; > + using R = __gnu_test::test_input_range<std::string>; > + R input_ranges[]{R(s1), R(s2), R(s3)}; > + __gnu_test::test_input_range<R> rr(input_ranges); > + namespace pmr = std::pmr; > + __gnu_test::memory_resource res; > +#if _GLIBCXX_USE_CXX11_ABI > + auto vvs = std::ranges::to<pmr::vector<pmr::vector<pmr::string>>>(rr, > &res); > + auto str_alloc = pmr::polymorphic_allocator<char>(&res); > +#else > + auto vvs = std::ranges::to<pmr::vector<pmr::vector<std::string>>>(rr, > &res); > + auto str_alloc = std::allocator<char>(); > +#endif > + VERIFY( vvs[1][1] == "VI" ); > + VERIFY( vvs[2][2] == "0x0b" ); > + VERIFY( vvs[0].get_allocator().resource() == &res ); > + VERIFY( vvs[2][2].get_allocator() == str_alloc ); > +} > + > +void > +test_lwg3984() > +{ > + std::vector<std::vector<int>> v; > + auto r = std::views::all(std::move(v)); > + auto l = std::ranges::to<std::list<std::list<int>>>(r); > + VERIFY(l.empty()); > +} > + > +void > +test_nodiscard() > +{ > + std::vector<int> v; > + std::ranges::to<std::vector<long>>(v); // { dg-warning "ignoring return" } > + std::ranges::to<std::vector>(v); // { dg-warning "ignoring return" } > + std::ranges::to<std::vector<long>>(); // { dg-warning "ignoring return" } > + std::ranges::to<std::vector>(); // { dg-warning "ignoring return" } > +} > + > +int main() > +{ > + test_p1206r7_examples(); > + test_example_1(); > + test_2_1_1(); > + test_2_1_2(); > + test_2_1_3(); > + test_2_1_4(); > + test_2_2(); > + test_lwg3984(); > + test_nodiscard(); > +} > diff --git a/libstdc++-v3/testsuite/std/ranges/conv/2_neg.cc > b/libstdc++-v3/testsuite/std/ranges/conv/2_neg.cc > new file mode 100644 > index 00000000000..1e5f6f18408 > --- /dev/null > +++ b/libstdc++-v3/testsuite/std/ranges/conv/2_neg.cc > @@ -0,0 +1,24 @@ > +// { dg-do compile { target c++23 } } > + > +// C++23 26.5.7 Range conversions [range.utility.conv] > + > +#include <ranges> > +#include <vector> > +#include <testsuite_allocator.h> > + > +void > +test_2_1_5() > +{ > + // (2.1.5) Otherwise, the program is ill-formed. > + > + using Alloc = __gnu_test::uneq_allocator<int>; > + using Vec = std::vector<int, Alloc>; > + > + std::vector<int> v; > + (void) std::ranges::to<Vec>(v, v.get_allocator()); // { dg-error "here" } > + > + (void) std::ranges::to<Vec>(Vec{}, 1, 2, 3, 4, 5, 6); // { dg-error "here" > } > +} > + > +// { dg-error "static assertion failed" "" { target *-*-* } 0 } > +// { dg-prune-output "no matching function" } > diff --git a/libstdc++-v3/testsuite/std/ranges/conv/version.cc > b/libstdc++-v3/testsuite/std/ranges/conv/version.cc > new file mode 100644 > index 00000000000..33736807eb4 > --- /dev/null > +++ b/libstdc++-v3/testsuite/std/ranges/conv/version.cc > @@ -0,0 +1,19 @@ > +// { dg-do preprocess { target c++23 } } > +// { dg-add-options no_pch } > + > +#include <ranges> > + > +#ifndef __cpp_lib_ranges_to_container > +# error "Feature test macro for ranges_to_container is missing in <ranges>" > +#elif __cpp_lib_ranges_to_container < 202202L > +# error "Feature test macro for ranges_to_container has wrong value in > <ranges>" > +#endif > + > +#undef __cpp_lib_ranges_to_container > +#include <version> > + > +#ifndef __cpp_lib_ranges_to_container > +# error "Feature test macro for ranges_to_container is missing in <version>" > +#elif __cpp_lib_ranges_to_container < 202202L > +# error "Feature test macro for ranges_to_container has wrong value in > <version>" > +#endif > -- > 2.42.0 > >