1c3648f37Svarconst //===----------------------------------------------------------------------===// 2c3648f37Svarconst // 3c3648f37Svarconst // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4c3648f37Svarconst // See https://llvm.org/LICENSE.txt for license information. 5c3648f37Svarconst // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6c3648f37Svarconst // 7c3648f37Svarconst //===----------------------------------------------------------------------===// 8c3648f37Svarconst 964addd65SStephan T. Lavavej // MSVC warning C4244: 'argument': conversion from '_Ty' to 'int', possible loss of data 1064addd65SStephan T. Lavavej // ADDITIONAL_COMPILE_FLAGS(cl-style-warnings): /wd4244 1164addd65SStephan T. Lavavej 12c3648f37Svarconst // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 13c3648f37Svarconst 14c3648f37Svarconst // template<class C, input_range R, class... Args> requires (!view<C>) 15c3648f37Svarconst // constexpr C to(R&& r, Args&&... args); // Since C++23 16c3648f37Svarconst 17c3648f37Svarconst #include <ranges> 18c3648f37Svarconst 19c3648f37Svarconst #include <algorithm> 20c3648f37Svarconst #include <array> 21c3648f37Svarconst #include <cassert> 22c3648f37Svarconst #include <vector> 23c3648f37Svarconst #include "container.h" 24c3648f37Svarconst #include "test_iterators.h" 258b9a9866SKonstantin Varlamov #include "test_macros.h" 26c3648f37Svarconst #include "test_range.h" 27c3648f37Svarconst 28c3648f37Svarconst template <class Container, class Range, class... Args> 29c3648f37Svarconst concept HasTo = requires (Range&& range, Args ...args) { 30c3648f37Svarconst std::ranges::to<Container>(std::forward<Range>(range), std::forward<Args>(args)...); 31c3648f37Svarconst }; 32c3648f37Svarconst 33c3648f37Svarconst struct InputRange { 34c3648f37Svarconst int x = 0; 35c3648f37Svarconst constexpr cpp20_input_iterator<int*> begin() { 36c3648f37Svarconst return cpp20_input_iterator<int*>(&x); 37c3648f37Svarconst } 38c3648f37Svarconst constexpr sentinel_wrapper<cpp20_input_iterator<int*>> end() { 39c3648f37Svarconst return sentinel_wrapper<cpp20_input_iterator<int*>>(begin()); 40c3648f37Svarconst } 41c3648f37Svarconst }; 42c3648f37Svarconst static_assert(std::ranges::input_range<InputRange>); 43c3648f37Svarconst 44c3648f37Svarconst struct common_cpp20_input_iterator { 45c3648f37Svarconst using value_type = int; 46c3648f37Svarconst using difference_type = long long; 47c3648f37Svarconst using iterator_concept = std::input_iterator_tag; 48c3648f37Svarconst // Deliberately not defining `iterator_category` to make sure this class satisfies the `input_iterator` concept but 49c3648f37Svarconst // would fail `derived_from<iterator_category, input_iterator_tag>`. 50c3648f37Svarconst 51c3648f37Svarconst int x = 0; 52c3648f37Svarconst 53c3648f37Svarconst // Copyable so that it can be used as a sentinel against itself. 54c3648f37Svarconst constexpr decltype(auto) operator*() const { return x; } 55c3648f37Svarconst constexpr common_cpp20_input_iterator& operator++() { return *this; } 56c3648f37Svarconst constexpr void operator++(int) {} 57c3648f37Svarconst constexpr friend bool operator==(common_cpp20_input_iterator, common_cpp20_input_iterator) { return true; } 58c3648f37Svarconst }; 59c3648f37Svarconst static_assert(std::input_iterator<common_cpp20_input_iterator>); 60c3648f37Svarconst static_assert(std::sentinel_for<common_cpp20_input_iterator, common_cpp20_input_iterator>); 61c3648f37Svarconst template <class T> 62c3648f37Svarconst concept HasIteratorCategory = requires { 63c3648f37Svarconst typename std::iterator_traits<T>::iterator_category; 64c3648f37Svarconst }; 65c3648f37Svarconst static_assert(!HasIteratorCategory<common_cpp20_input_iterator>); 66c3648f37Svarconst 67c3648f37Svarconst struct CommonInputRange { 68c3648f37Svarconst int x = 0; 69c3648f37Svarconst constexpr common_cpp20_input_iterator begin() { return {}; } 70c3648f37Svarconst constexpr common_cpp20_input_iterator end() { return begin(); } 71c3648f37Svarconst }; 72c3648f37Svarconst static_assert(std::ranges::input_range<CommonInputRange>); 73c3648f37Svarconst static_assert(std::ranges::common_range<CommonInputRange>); 74c3648f37Svarconst 75c3648f37Svarconst struct CommonRange { 76c3648f37Svarconst int x = 0; 77c3648f37Svarconst constexpr forward_iterator<int*> begin() { 78c3648f37Svarconst return forward_iterator<int*>(&x); 79c3648f37Svarconst } 80c3648f37Svarconst constexpr forward_iterator<int*> end() { 81c3648f37Svarconst return begin(); 82c3648f37Svarconst } 83c3648f37Svarconst }; 84c3648f37Svarconst static_assert(std::ranges::input_range<CommonRange>); 85c3648f37Svarconst static_assert(std::ranges::common_range<CommonRange>); 86c3648f37Svarconst 87c3648f37Svarconst struct NonCommonRange { 88c3648f37Svarconst int x = 0; 89c3648f37Svarconst constexpr forward_iterator<int*> begin() { 90c3648f37Svarconst return forward_iterator<int*>(&x); 91c3648f37Svarconst } 92c3648f37Svarconst constexpr sentinel_wrapper<forward_iterator<int*>> end() { 93c3648f37Svarconst return sentinel_wrapper<forward_iterator<int*>>(begin()); 94c3648f37Svarconst } 95c3648f37Svarconst }; 96c3648f37Svarconst static_assert(std::ranges::input_range<NonCommonRange>); 97c3648f37Svarconst static_assert(!std::ranges::common_range<NonCommonRange>); 98c3648f37Svarconst static_assert(std::derived_from< 99c3648f37Svarconst typename std::iterator_traits<std::ranges::iterator_t<NonCommonRange>>::iterator_category, 100c3648f37Svarconst std::input_iterator_tag>); 101c3648f37Svarconst 102c3648f37Svarconst using ContainerT = int; 103c3648f37Svarconst static_assert(!std::ranges::view<ContainerT>); 104c3648f37Svarconst static_assert(HasTo<ContainerT, InputRange>); 105c3648f37Svarconst static_assert(!HasTo<test_view<forward_iterator>, InputRange>); 106c3648f37Svarconst 107c3648f37Svarconst // Note: it's not possible to check the `input_range` constraint because if it's not satisfied, the pipe adaptor 108c3648f37Svarconst // overload hijacks the call (it takes unconstrained variadic arguments). 109c3648f37Svarconst 110c3648f37Svarconst // Check the exact constraints for each one of the cases inside `ranges::to`. 111c3648f37Svarconst 112c3648f37Svarconst struct Empty {}; 113c3648f37Svarconst 114c3648f37Svarconst struct Fallback { 115c3648f37Svarconst using value_type = int; 116c3648f37Svarconst 117c3648f37Svarconst CtrChoice ctr_choice = CtrChoice::Invalid; 118c3648f37Svarconst int x = 0; 119c3648f37Svarconst 120c3648f37Svarconst constexpr Fallback() : ctr_choice(CtrChoice::DefaultCtrAndInsert) {} 121c3648f37Svarconst constexpr Fallback(Empty) : ctr_choice(CtrChoice::DefaultCtrAndInsert) {} 122c3648f37Svarconst 123c3648f37Svarconst constexpr void push_back(value_type) {} 124c3648f37Svarconst constexpr value_type* begin() { return &x; } 125c3648f37Svarconst constexpr value_type* end() { return &x; } 1268b9a9866SKonstantin Varlamov std::size_t size() const { return 0; } 127c3648f37Svarconst }; 128c3648f37Svarconst 129c3648f37Svarconst struct CtrDirectOrFallback : Fallback { 130c3648f37Svarconst using Fallback::Fallback; 131c3648f37Svarconst constexpr CtrDirectOrFallback(InputRange&&, int = 0) { ctr_choice = CtrChoice::DirectCtr; } 132c3648f37Svarconst }; 133c3648f37Svarconst 134c3648f37Svarconst struct CtrFromRangeTOrFallback : Fallback { 135c3648f37Svarconst using Fallback::Fallback; 136c3648f37Svarconst constexpr CtrFromRangeTOrFallback(std::from_range_t, InputRange&&, int = 0) { ctr_choice = CtrChoice::FromRangeT; } 137c3648f37Svarconst }; 138c3648f37Svarconst 139c3648f37Svarconst struct CtrBeginEndPairOrFallback : Fallback { 140c3648f37Svarconst using Fallback::Fallback; 141c3648f37Svarconst template <class Iter> 142c3648f37Svarconst constexpr CtrBeginEndPairOrFallback(Iter, Iter, int = 0) { ctr_choice = CtrChoice::BeginEndPair; } 143c3648f37Svarconst }; 144c3648f37Svarconst 145c3648f37Svarconst template <bool HasSize> 146c3648f37Svarconst struct MaybeSizedRange { 147c3648f37Svarconst int x = 0; 148c3648f37Svarconst constexpr forward_iterator<int*> begin() { return forward_iterator<int*>(&x); } 149c3648f37Svarconst constexpr forward_iterator<int*> end() { return begin(); } 150c3648f37Svarconst 151c3648f37Svarconst constexpr std::size_t size() const 152c3648f37Svarconst requires HasSize { 153c3648f37Svarconst return 0; 154c3648f37Svarconst } 155c3648f37Svarconst }; 156c3648f37Svarconst static_assert(std::ranges::sized_range<MaybeSizedRange<true>>); 157c3648f37Svarconst static_assert(!std::ranges::sized_range<MaybeSizedRange<false>>); 158c3648f37Svarconst 159c3648f37Svarconst template <bool HasCapacity = true, bool CapacityReturnsSizeT = true, 160c3648f37Svarconst bool HasMaxSize = true, bool MaxSizeReturnsSizeT = true> 161c3648f37Svarconst struct Reservable : Fallback { 162c3648f37Svarconst bool reserve_called = false; 163c3648f37Svarconst 164c3648f37Svarconst using Fallback::Fallback; 165c3648f37Svarconst 166c3648f37Svarconst constexpr std::size_t capacity() const 167c3648f37Svarconst requires (HasCapacity && CapacityReturnsSizeT) { 168c3648f37Svarconst return 0; 169c3648f37Svarconst } 170c3648f37Svarconst constexpr int capacity() const 171c3648f37Svarconst requires (HasCapacity && !CapacityReturnsSizeT) { 172c3648f37Svarconst return 0; 173c3648f37Svarconst } 174c3648f37Svarconst 175c3648f37Svarconst constexpr std::size_t max_size() const 176c3648f37Svarconst requires (HasMaxSize && MaxSizeReturnsSizeT) { 177c3648f37Svarconst return 0; 178c3648f37Svarconst } 179c3648f37Svarconst constexpr int max_size() const 180c3648f37Svarconst requires (HasMaxSize && !MaxSizeReturnsSizeT) { 181c3648f37Svarconst return 0; 182c3648f37Svarconst } 183c3648f37Svarconst 184c3648f37Svarconst constexpr void reserve(std::size_t) { 185c3648f37Svarconst reserve_called = true; 186c3648f37Svarconst } 187c3648f37Svarconst }; 1888b9a9866SKonstantin Varlamov LIBCPP_STATIC_ASSERT(std::ranges::__reservable_container<Reservable<>>); 189c3648f37Svarconst 190c3648f37Svarconst constexpr void test_constraints() { 191c3648f37Svarconst { // Case 1 -- construct directly from the range. 192c3648f37Svarconst { // (range) 193c3648f37Svarconst auto result = std::ranges::to<CtrDirectOrFallback>(InputRange()); 194c3648f37Svarconst assert(result.ctr_choice == CtrChoice::DirectCtr); 195c3648f37Svarconst } 196c3648f37Svarconst 197c3648f37Svarconst { // (range, arg) 198c3648f37Svarconst auto result = std::ranges::to<CtrDirectOrFallback>(InputRange(), 1); 199c3648f37Svarconst assert(result.ctr_choice == CtrChoice::DirectCtr); 200c3648f37Svarconst } 201c3648f37Svarconst 202c3648f37Svarconst { // (range, convertible-to-arg) 203c3648f37Svarconst auto result = std::ranges::to<CtrDirectOrFallback>(InputRange(), 1.0); 204c3648f37Svarconst assert(result.ctr_choice == CtrChoice::DirectCtr); 205c3648f37Svarconst } 206c3648f37Svarconst 207c3648f37Svarconst { // (range, BAD_arg) 208c3648f37Svarconst auto result = std::ranges::to<CtrDirectOrFallback>(InputRange(), Empty()); 209c3648f37Svarconst assert(result.ctr_choice == CtrChoice::DefaultCtrAndInsert); 210c3648f37Svarconst } 211c3648f37Svarconst } 212c3648f37Svarconst 213c3648f37Svarconst { // Case 2 -- construct using the `from_range_t` tagged constructor. 214c3648f37Svarconst { // (range) 215c3648f37Svarconst auto result = std::ranges::to<CtrFromRangeTOrFallback>(InputRange()); 216c3648f37Svarconst assert(result.ctr_choice == CtrChoice::FromRangeT); 217c3648f37Svarconst } 218c3648f37Svarconst 219c3648f37Svarconst { // (range, arg) 220c3648f37Svarconst auto result = std::ranges::to<CtrFromRangeTOrFallback>(InputRange(), 1); 221c3648f37Svarconst assert(result.ctr_choice == CtrChoice::FromRangeT); 222c3648f37Svarconst } 223c3648f37Svarconst 224c3648f37Svarconst { // (range, convertible-to-arg) 225c3648f37Svarconst auto result = std::ranges::to<CtrFromRangeTOrFallback>(InputRange(), 1.0); 226c3648f37Svarconst assert(result.ctr_choice == CtrChoice::FromRangeT); 227c3648f37Svarconst } 228c3648f37Svarconst 229c3648f37Svarconst { // (range, BAD_arg) 230c3648f37Svarconst auto result = std::ranges::to<CtrFromRangeTOrFallback>(InputRange(), Empty()); 231c3648f37Svarconst assert(result.ctr_choice == CtrChoice::DefaultCtrAndInsert); 232c3648f37Svarconst } 233c3648f37Svarconst } 234c3648f37Svarconst 235c3648f37Svarconst { // Case 3 -- construct from a begin-end iterator pair. 236c3648f37Svarconst { // (range) 237c3648f37Svarconst auto result = std::ranges::to<CtrBeginEndPairOrFallback>(CommonRange()); 238c3648f37Svarconst assert(result.ctr_choice == CtrChoice::BeginEndPair); 239c3648f37Svarconst } 240c3648f37Svarconst 241c3648f37Svarconst { // (range, arg) 242c3648f37Svarconst auto result = std::ranges::to<CtrBeginEndPairOrFallback>(CommonRange(), 1); 243c3648f37Svarconst assert(result.ctr_choice == CtrChoice::BeginEndPair); 244c3648f37Svarconst } 245c3648f37Svarconst 246c3648f37Svarconst { // (range, convertible-to-arg) 247c3648f37Svarconst auto result = std::ranges::to<CtrBeginEndPairOrFallback>(CommonRange(), 1.0); 248c3648f37Svarconst assert(result.ctr_choice == CtrChoice::BeginEndPair); 249c3648f37Svarconst } 250c3648f37Svarconst 251c3648f37Svarconst { // (BAD_range) -- not a common range. 252c3648f37Svarconst auto result = std::ranges::to<CtrBeginEndPairOrFallback>(NonCommonRange()); 253c3648f37Svarconst assert(result.ctr_choice == CtrChoice::DefaultCtrAndInsert); 254c3648f37Svarconst } 255c3648f37Svarconst 256c3648f37Svarconst { // (BAD_range) -- iterator type not derived from `input_iterator_tag`. 257c3648f37Svarconst auto result = std::ranges::to<CtrBeginEndPairOrFallback>(CommonInputRange()); 258c3648f37Svarconst assert(result.ctr_choice == CtrChoice::DefaultCtrAndInsert); 259c3648f37Svarconst } 260c3648f37Svarconst 261c3648f37Svarconst { // (range, BAD_arg) 262c3648f37Svarconst auto result = std::ranges::to<CtrBeginEndPairOrFallback>(CommonRange(), Empty()); 263c3648f37Svarconst assert(result.ctr_choice == CtrChoice::DefaultCtrAndInsert); 264c3648f37Svarconst } 265c3648f37Svarconst } 266c3648f37Svarconst 267c3648f37Svarconst { // Case 4 -- default-construct (or construct from the extra arguments) and insert, reserving the size if possible. 268c3648f37Svarconst // Note: it's not possible to check the constraints on the default constructor using this approach because there is 269c3648f37Svarconst // nothing to fall back to -- the call will result in a hard error. 270c3648f37Svarconst // However, it's possible to check the constraints on reserving the capacity. 271c3648f37Svarconst 272c3648f37Svarconst { // All constraints satisfied. 273c3648f37Svarconst using C = Reservable<>; 274c3648f37Svarconst auto result = std::ranges::to<C>(MaybeSizedRange<true>()); 275c3648f37Svarconst assert(result.reserve_called); 276c3648f37Svarconst } 277c3648f37Svarconst 278c3648f37Svarconst { // !sized_range 279c3648f37Svarconst using C = Reservable<>; 280c3648f37Svarconst auto result = std::ranges::to<C>(MaybeSizedRange<false>()); 281c3648f37Svarconst assert(!result.reserve_called); 282c3648f37Svarconst } 283c3648f37Svarconst 284c3648f37Svarconst { // Missing `capacity`. 285c3648f37Svarconst using C = Reservable</*HasCapacity=*/false>; 286c3648f37Svarconst auto result = std::ranges::to<C>(MaybeSizedRange<true>()); 287c3648f37Svarconst assert(!result.reserve_called); 288c3648f37Svarconst } 289c3648f37Svarconst 290c3648f37Svarconst { // `capacity` doesn't return `size_type`. 291c3648f37Svarconst using C = Reservable</*HasCapacity=*/true, /*CapacityReturnsSizeT=*/false>; 292c3648f37Svarconst auto result = std::ranges::to<C>(MaybeSizedRange<true>()); 293c3648f37Svarconst assert(!result.reserve_called); 294c3648f37Svarconst } 295c3648f37Svarconst 296c3648f37Svarconst { // Missing `max_size`. 297c3648f37Svarconst using C = Reservable</*HasCapacity=*/true, /*CapacityReturnsSizeT=*/true, /*HasMaxSize=*/false>; 298c3648f37Svarconst auto result = std::ranges::to<C>(MaybeSizedRange<true>()); 299c3648f37Svarconst assert(!result.reserve_called); 300c3648f37Svarconst } 301c3648f37Svarconst 302c3648f37Svarconst { // `max_size` doesn't return `size_type`. 303c3648f37Svarconst using C = Reservable< 304c3648f37Svarconst /*HasCapacity=*/true, /*CapacityReturnsSizeT=*/true, /*HasMaxSize=*/true, /*MaxSizeReturnsSizeT=*/false>; 305c3648f37Svarconst auto result = std::ranges::to<C>(MaybeSizedRange<true>()); 306c3648f37Svarconst assert(!result.reserve_called); 307c3648f37Svarconst } 308c3648f37Svarconst } 309c3648f37Svarconst } 310c3648f37Svarconst 311c3648f37Svarconst constexpr void test_ctr_choice_order() { 312c3648f37Svarconst std::array in = {1, 2, 3, 4, 5}; 313c3648f37Svarconst int arg1 = 42; 314c3648f37Svarconst char arg2 = 'a'; 315c3648f37Svarconst 316c3648f37Svarconst { // Case 1 -- construct directly from the given range. 317c3648f37Svarconst { 318c3648f37Svarconst using C = Container<int, CtrChoice::DirectCtr>; 319c3648f37Svarconst std::same_as<C> decltype(auto) result = std::ranges::to<C>(in); 320c3648f37Svarconst 321c3648f37Svarconst assert(result.ctr_choice == CtrChoice::DirectCtr); 322c3648f37Svarconst assert(std::ranges::equal(result, in)); 323c3648f37Svarconst assert((in | std::ranges::to<C>()) == result); 324c3648f37Svarconst auto closure = std::ranges::to<C>(); 325c3648f37Svarconst assert((in | closure) == result); 326c3648f37Svarconst } 327c3648f37Svarconst 328c3648f37Svarconst { // Extra arguments. 329c3648f37Svarconst using C = Container<int, CtrChoice::DirectCtr>; 330c3648f37Svarconst std::same_as<C> decltype(auto) result = std::ranges::to<C>(in, arg1, arg2); 331c3648f37Svarconst 332c3648f37Svarconst assert(result.ctr_choice == CtrChoice::DirectCtr); 333c3648f37Svarconst assert(std::ranges::equal(result, in)); 334c3648f37Svarconst assert(result.extra_arg1 == arg1); 335c3648f37Svarconst assert(result.extra_arg2 == arg2); 336c3648f37Svarconst assert((in | std::ranges::to<C>(arg1, arg2)) == result); 337c3648f37Svarconst auto closure = std::ranges::to<C>(arg1, arg2); 338c3648f37Svarconst assert((in | closure) == result); 339c3648f37Svarconst } 340c3648f37Svarconst } 341c3648f37Svarconst 342c3648f37Svarconst { // Case 2 -- construct using the `from_range_t` tag. 343c3648f37Svarconst { 344c3648f37Svarconst using C = Container<int, CtrChoice::FromRangeT>; 345c3648f37Svarconst std::same_as<C> decltype(auto) result = std::ranges::to<C>(in); 346c3648f37Svarconst 347c3648f37Svarconst assert(result.ctr_choice == CtrChoice::FromRangeT); 348c3648f37Svarconst assert(std::ranges::equal(result, in)); 349c3648f37Svarconst assert((in | std::ranges::to<C>()) == result); 350c3648f37Svarconst auto closure = std::ranges::to<C>(); 351c3648f37Svarconst assert((in | closure) == result); 352c3648f37Svarconst } 353c3648f37Svarconst 354c3648f37Svarconst { // Extra arguments. 355c3648f37Svarconst using C = Container<int, CtrChoice::FromRangeT>; 356c3648f37Svarconst std::same_as<C> decltype(auto) result = std::ranges::to<C>(in, arg1, arg2); 357c3648f37Svarconst 358c3648f37Svarconst assert(result.ctr_choice == CtrChoice::FromRangeT); 359c3648f37Svarconst assert(std::ranges::equal(result, in)); 360c3648f37Svarconst assert(result.extra_arg1 == arg1); 361c3648f37Svarconst assert(result.extra_arg2 == arg2); 362c3648f37Svarconst assert((in | std::ranges::to<C>(arg1, arg2)) == result); 363c3648f37Svarconst auto closure = std::ranges::to<C>(arg1, arg2); 364c3648f37Svarconst assert((in | closure) == result); 365c3648f37Svarconst } 366c3648f37Svarconst } 367c3648f37Svarconst 368c3648f37Svarconst { // Case 3 -- construct from a begin-end pair. 369c3648f37Svarconst { 370c3648f37Svarconst using C = Container<int, CtrChoice::BeginEndPair>; 371c3648f37Svarconst std::same_as<C> decltype(auto) result = std::ranges::to<C>(in); 372c3648f37Svarconst 373c3648f37Svarconst assert(result.ctr_choice == CtrChoice::BeginEndPair); 374c3648f37Svarconst assert(std::ranges::equal(result, in)); 375c3648f37Svarconst assert((in | std::ranges::to<C>()) == result); 376c3648f37Svarconst auto closure = std::ranges::to<C>(); 377c3648f37Svarconst assert((in | closure) == result); 378c3648f37Svarconst } 379c3648f37Svarconst 380c3648f37Svarconst { // Extra arguments. 381c3648f37Svarconst using C = Container<int, CtrChoice::BeginEndPair>; 382c3648f37Svarconst std::same_as<C> decltype(auto) result = std::ranges::to<C>(in, arg1, arg2); 383c3648f37Svarconst 384c3648f37Svarconst assert(result.ctr_choice == CtrChoice::BeginEndPair); 385c3648f37Svarconst assert(std::ranges::equal(result, in)); 386c3648f37Svarconst assert(result.extra_arg1 == arg1); 387c3648f37Svarconst assert(result.extra_arg2 == arg2); 388c3648f37Svarconst assert((in | std::ranges::to<C>(arg1, arg2)) == result); 389c3648f37Svarconst auto closure = std::ranges::to<C>(arg1, arg2); 390c3648f37Svarconst assert((in | closure) == result); 391c3648f37Svarconst } 392c3648f37Svarconst } 393c3648f37Svarconst 394c3648f37Svarconst { // Case 4 -- default-construct then insert elements. 395*7c721999SXiaoyang Liu auto case_4 = [in, arg1, arg2]<auto InserterChoice, bool CanReserve>() { 396*7c721999SXiaoyang Liu using C = Container<int, CtrChoice::DefaultCtrAndInsert, InserterChoice, CanReserve>; 397c3648f37Svarconst { 398*7c721999SXiaoyang Liu [[maybe_unused]] std::same_as<C> decltype(auto) result = std::ranges::to<C>(in); 399c3648f37Svarconst 400c3648f37Svarconst assert(result.ctr_choice == CtrChoice::DefaultCtrAndInsert); 401*7c721999SXiaoyang Liu assert(result.inserter_choice == InserterChoice); 402c3648f37Svarconst assert(std::ranges::equal(result, in)); 403c3648f37Svarconst 404*7c721999SXiaoyang Liu if constexpr (CanReserve) { 405c3648f37Svarconst assert(result.called_reserve); 406*7c721999SXiaoyang Liu } else { 407c3648f37Svarconst assert(!result.called_reserve); 408*7c721999SXiaoyang Liu } 409*7c721999SXiaoyang Liu 410c3648f37Svarconst assert((in | std::ranges::to<C>()) == result); 411*7c721999SXiaoyang Liu [[maybe_unused]] auto closure = std::ranges::to<C>(); 412c3648f37Svarconst assert((in | closure) == result); 413c3648f37Svarconst } 414c3648f37Svarconst 415*7c721999SXiaoyang Liu { // Extra arguments 416*7c721999SXiaoyang Liu [[maybe_unused]] std::same_as<C> decltype(auto) result = std::ranges::to<C>(in, arg1, arg2); 417c3648f37Svarconst 418c3648f37Svarconst assert(result.ctr_choice == CtrChoice::DefaultCtrAndInsert); 419*7c721999SXiaoyang Liu assert(result.inserter_choice == InserterChoice); 420c3648f37Svarconst assert(std::ranges::equal(result, in)); 421c3648f37Svarconst assert(result.extra_arg1 == arg1); 422c3648f37Svarconst assert(result.extra_arg2 == arg2); 423*7c721999SXiaoyang Liu 424*7c721999SXiaoyang Liu if constexpr (CanReserve) { 425*7c721999SXiaoyang Liu assert(result.called_reserve); 426*7c721999SXiaoyang Liu } else { 427*7c721999SXiaoyang Liu assert(!result.called_reserve); 428*7c721999SXiaoyang Liu } 429*7c721999SXiaoyang Liu 430c3648f37Svarconst assert((in | std::ranges::to<C>(arg1, arg2)) == result); 431*7c721999SXiaoyang Liu [[maybe_unused]] auto closure = std::ranges::to<C>(arg1, arg2); 432c3648f37Svarconst assert((in | closure) == result); 433c3648f37Svarconst } 434*7c721999SXiaoyang Liu }; 435*7c721999SXiaoyang Liu 436*7c721999SXiaoyang Liu case_4.operator()<InserterChoice::Insert, false>(); 437*7c721999SXiaoyang Liu case_4.operator()<InserterChoice::Insert, true>(); 438*7c721999SXiaoyang Liu case_4.operator()<InserterChoice::Emplace, false>(); 439*7c721999SXiaoyang Liu case_4.operator()<InserterChoice::Emplace, true>(); 440*7c721999SXiaoyang Liu case_4.operator()<InserterChoice::PushBack, false>(); 441*7c721999SXiaoyang Liu case_4.operator()<InserterChoice::PushBack, true>(); 442*7c721999SXiaoyang Liu case_4.operator()<InserterChoice::EmplaceBack, false>(); 443*7c721999SXiaoyang Liu case_4.operator()<InserterChoice::EmplaceBack, true>(); 444c3648f37Svarconst } 445c3648f37Svarconst } 446c3648f37Svarconst 447c3648f37Svarconst template <CtrChoice Rank> 448c3648f37Svarconst struct NotARange { 449c3648f37Svarconst using value_type = int; 450c3648f37Svarconst 451c3648f37Svarconst constexpr NotARange(std::ranges::input_range auto&&) 452c3648f37Svarconst requires (Rank >= CtrChoice::DirectCtr) 453c3648f37Svarconst {} 454c3648f37Svarconst 455c3648f37Svarconst constexpr NotARange(std::from_range_t, std::ranges::input_range auto&&) 456c3648f37Svarconst requires (Rank >= CtrChoice::FromRangeT) 457c3648f37Svarconst {} 458c3648f37Svarconst 459c3648f37Svarconst template <class Iter> 460c3648f37Svarconst constexpr NotARange(Iter, Iter) 461c3648f37Svarconst requires (Rank >= CtrChoice::BeginEndPair) 462c3648f37Svarconst {} 463c3648f37Svarconst 464c3648f37Svarconst constexpr NotARange() 465c3648f37Svarconst requires (Rank >= CtrChoice::DefaultCtrAndInsert) 466c3648f37Svarconst = default; 467c3648f37Svarconst 468c3648f37Svarconst constexpr void push_back(int) {} 469c3648f37Svarconst }; 470c3648f37Svarconst 471c3648f37Svarconst static_assert(!std::ranges::range<NotARange<CtrChoice::DirectCtr>>); 472c3648f37Svarconst 473c3648f37Svarconst constexpr void test_lwg_3785() { 474c3648f37Svarconst // Test LWG 3785 ("`ranges::to` is over-constrained on the destination type being a range") -- make sure it's possible 475c3648f37Svarconst // to convert the given input range to a non-range type. 476c3648f37Svarconst std::array in = {1, 2, 3, 4, 5}; 477c3648f37Svarconst 478c3648f37Svarconst { 479c3648f37Svarconst using C = NotARange<CtrChoice::DirectCtr>; 480c3648f37Svarconst [[maybe_unused]] std::same_as<C> decltype(auto) result = std::ranges::to<C>(in); 481c3648f37Svarconst } 482c3648f37Svarconst 483c3648f37Svarconst { 484c3648f37Svarconst using C = NotARange<CtrChoice::FromRangeT>; 485c3648f37Svarconst [[maybe_unused]] std::same_as<C> decltype(auto) result = std::ranges::to<C>(in); 486c3648f37Svarconst } 487c3648f37Svarconst 488c3648f37Svarconst { 489c3648f37Svarconst using C = NotARange<CtrChoice::BeginEndPair>; 490c3648f37Svarconst [[maybe_unused]] std::same_as<C> decltype(auto) result = std::ranges::to<C>(in); 491c3648f37Svarconst } 492c3648f37Svarconst 493c3648f37Svarconst { 494c3648f37Svarconst using C = NotARange<CtrChoice::DefaultCtrAndInsert>; 495c3648f37Svarconst [[maybe_unused]] std::same_as<C> decltype(auto) result = std::ranges::to<C>(in); 496c3648f37Svarconst } 497c3648f37Svarconst } 498c3648f37Svarconst 499c3648f37Svarconst constexpr void test_recursive() { 500c3648f37Svarconst using C1 = Container<int, CtrChoice::DirectCtr>; 501c3648f37Svarconst using C2 = Container<C1, CtrChoice::FromRangeT>; 502c3648f37Svarconst using C3 = Container<C2, CtrChoice::BeginEndPair>; 503c3648f37Svarconst using C4 = Container<C3, CtrChoice::DefaultCtrAndInsert, InserterChoice::PushBack>; 504c3648f37Svarconst using A1 = std::array<int, 4>; 505c3648f37Svarconst using A2 = std::array<A1, 3>; 506c3648f37Svarconst using A3 = std::array<A2, 2>; 507c3648f37Svarconst using A4 = std::array<A3, 2>; 508c3648f37Svarconst 509c3648f37Svarconst A4 in = {}; 510c3648f37Svarconst { // Fill the nested array with incremental values. 511c3648f37Svarconst int x = 0; 512c3648f37Svarconst for (auto& a3 : in) { 513c3648f37Svarconst for (auto& a2 : a3) { 514c3648f37Svarconst for (auto& a1 : a2) { 515c3648f37Svarconst for (int& el : a1) { 516c3648f37Svarconst el = x++; 517c3648f37Svarconst } 518c3648f37Svarconst } 519c3648f37Svarconst } 520c3648f37Svarconst } 521c3648f37Svarconst } 522c3648f37Svarconst 523c3648f37Svarconst std::same_as<C4> decltype(auto) result = std::ranges::to<C4>(in); 524c3648f37Svarconst 525c3648f37Svarconst assert(result.ctr_choice == CtrChoice::DefaultCtrAndInsert); 526c3648f37Svarconst 527c3648f37Svarconst int expected_value = 0; 528c3648f37Svarconst for (auto& c3 : result) { 529c3648f37Svarconst assert(c3.ctr_choice == CtrChoice::BeginEndPair); 530c3648f37Svarconst 531c3648f37Svarconst for (auto& c2 : c3) { 532c3648f37Svarconst assert(c2.ctr_choice == CtrChoice::FromRangeT); 533c3648f37Svarconst 534c3648f37Svarconst for (auto& c1 : c2) { 535c3648f37Svarconst assert(c1.ctr_choice == CtrChoice::DirectCtr); 536c3648f37Svarconst 537c3648f37Svarconst for (int el : c1) { 538c3648f37Svarconst assert(el == expected_value); 539c3648f37Svarconst ++expected_value; 540c3648f37Svarconst } 541c3648f37Svarconst } 542c3648f37Svarconst } 543c3648f37Svarconst } 544c3648f37Svarconst 545c3648f37Svarconst assert((in | std::ranges::to<C4>()) == result); 546e74be35cSXiaoyang Liu 547e74be35cSXiaoyang Liu // LWG3984: ranges::to's recursion branch may be ill-formed 548e74be35cSXiaoyang Liu auto in_owning_view = std::views::all(std::move(in)); 549e74be35cSXiaoyang Liu static_assert(!std::ranges::viewable_range<decltype((in_owning_view))>); 550e74be35cSXiaoyang Liu assert(std::ranges::to<C4>(in_owning_view) == result); 551c3648f37Svarconst } 552c3648f37Svarconst 553c3648f37Svarconst constexpr bool test() { 554c3648f37Svarconst test_constraints(); 555c3648f37Svarconst test_ctr_choice_order(); 556c3648f37Svarconst test_lwg_3785(); 557c3648f37Svarconst test_recursive(); 558c3648f37Svarconst 559c3648f37Svarconst return true; 560c3648f37Svarconst } 561c3648f37Svarconst 562c3648f37Svarconst int main(int, char**) { 563c3648f37Svarconst test(); 564c3648f37Svarconst static_assert(test()); 565c3648f37Svarconst 566c3648f37Svarconst return 0; 567c3648f37Svarconst } 568