// { dg-do run { target c++23 } } // C++23 26.5.7 Range conversions [range.utility.conv] #include #include #include #include #include #include #include #include #include #include void test_p1206r7_examples() { using Alloc = __gnu_test::uneq_allocator; const Alloc alloc(303); const std::map 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>(l); // or vector{std::from_range, l}; //Specify an allocator auto b = ranges::to>(l, alloc); // or vector{std::from_range, l, alloc}; //deducing value_type auto c = ranges::to(l); // explicit conversion int -> long auto d = ranges::to>(l); //Supports converting associative container to sequence containers auto f = ranges::to(m); //Supports converting sequence containers to associative ones auto g = ranges::to(f); //Pipe syntax auto g2 = l | ranges::views::take(42) | ranges::to(); //Pipe syntax with allocator auto h = l | ranges::views::take(42) | ranges::to(alloc); //The pipe syntax also support specifying the type and conversions auto i = l | ranges::views::take(42) | ranges::to>(); // Nested ranges std::list> lst = {{0, 1, 2, 3}, {4, 5, 6, 7}}; auto vec1 = ranges::to>>(lst); auto vec2 = ranges::to>>(lst); VERIFY( vec == std::vector(std::ranges::begin(l), std::ranges::end(l)) ); static_assert(std::is_same_v>); VERIFY( b == (std::vector(vec.begin(), vec.end())) ); VERIFY( b.get_allocator() == alloc ); static_assert(std::is_same_v>); VERIFY( c == vec ); static_assert(std::is_same_v>); VERIFY( d == std::vector(vec.begin(), vec.end()) ); VERIFY( g == m ); static_assert(std::is_same_v>); VERIFY( g2 == vec ); static_assert(std::is_same_v>); VERIFY( h == b ); VERIFY( h.get_allocator() == alloc ); VERIFY( i == d ); static_assert(std::is_same_v>>); VERIFY( vec1[1][1] == 5 ); static_assert(std::is_same_v>>); 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>(); VERIFY( (is_same_v>) ); VERIFY( words == vector({"the", "quick", "brown", "fox"}) ); } template struct Cont1 { template requires std::constructible_from Cont1(R&& r, Args&&... args) : c(r, args...) { } C c; }; void test_2_1_1() { // (2.1.1) constructible_from std::vector v{1, 2, 3, 4}; auto v2 = std::ranges::to>(v); static_assert(std::is_same_v); VERIFY( v2 == v ); std::initializer_list il{5, 6, 7, 8}; v2 = std::ranges::to>(il); VERIFY( v2 == std::vector(il) ); v2 = std::ranges::to>(il, std::allocator{}); VERIFY( v2 == std::vector(il) ); using Alloc = __gnu_test::uneq_allocator; using V = std::vector; V v3({10, 11, 12, 13}, Alloc(14)); auto v4 = std::ranges::to(v3); static_assert(std::is_same_v); VERIFY( v4 == v3 ); VERIFY( v4.get_allocator() == v3.get_allocator() ); auto v5 = std::ranges::to(v3, Alloc(33)); VERIFY( v5 == v3 ); VERIFY( v5.get_allocator() == Alloc(33) ); auto v6 = std::ranges::to(il, Alloc(44)); VERIFY( v6 == V(il) ); VERIFY( v6.get_allocator() == Alloc(44) ); auto c = std::ranges::to>(V{1, 2, 3}); static_assert(std::is_same_v>); VERIFY( c.c == V({1, 2, 3}) ); auto c2 = std::ranges::to>(V{4, 5, 6}, Alloc(55)); static_assert(std::is_same_v>); VERIFY( c2.c == V({4, 5, 6}) ); VERIFY( c2.c.get_allocator() == Alloc(55) ); auto c3 = std::ranges::to>(il, Alloc(66)); static_assert(std::is_same_v>); VERIFY( c3.c == V(v2.begin(), v2.end()) ); VERIFY( c3.c.get_allocator() == Alloc(66) ); } template struct Cont2 { template requires std::constructible_from Cont2(std::from_range_t, R&& r, Args&&... args) : c(r, args...) { } C c; }; void test_2_1_2() { // (2.1.2) constructible_from using Alloc = __gnu_test::uneq_allocator; using V = std::vector; auto c = std::ranges::to>(V{1, 2, 3}); static_assert(std::is_same_v>); VERIFY( c.c == V({1, 2, 3}) ); auto c2 = std::ranges::to>(V{4, 5, 6}, Alloc(7)); static_assert(std::is_same_v>); VERIFY( c2.c == V({4, 5, 6}) ); VERIFY( c2.c.get_allocator() == Alloc(7) ); } template struct Cont3 { template requires std::same_as && std::constructible_from Cont3(It first, Sent last, Args&&... args) : c(first, last, args...) { } C c; }; void test_2_1_3() { // (2.1.3) constructible_from, sentinel_t using Alloc = __gnu_test::uneq_allocator; using V = std::vector; std::list l{1u, 2u, 3u}; auto c = std::ranges::to>(l); static_assert(std::is_same_v>); VERIFY( c.c == V(l.begin(), l.end()) ); std::list l2{4l, 5l, 6l}; auto c2 = std::ranges::to>(l2, Alloc(78)); static_assert(std::is_same_v>); VERIFY( c2.c == V(l2.begin(), l2.end()) ); VERIFY( c2.c.get_allocator() == Alloc(78) ); } enum AppendKind { None, EmplaceBack, PushBack, Emplace, Insert }; template struct Cont4 { // Only support construction with no args or an allocator. // This forces the use of either emplace_back, push_back, emplace or insert. Cont4() { } Cont4(typename C::allocator_type a) : c(a) { } template requires (Kind <= EmplaceBack) && requires(C& c, T&& t) { c.emplace_back(std::forward(t)); } void emplace_back(T&& t) { kind = EmplaceBack; c.emplace_back(std::forward(t)); } template requires (Kind <= PushBack) && requires(C& c, T&& t) { c.push_back(std::forward(t)); } void push_back(T&& t) { kind = PushBack; c.push_back(std::forward(t)); } template requires (Kind <= Emplace) && requires(C& c, T&& t) { c.emplace(c.end(), std::forward(t)); } void emplace(typename C::iterator pos, T&& t) { kind = Emplace; c.emplace(pos, std::forward(t)); } template void insert(typename C::iterator pos, T&& t) { kind = Insert; c.insert(pos, std::forward(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; } // Satisfying sized_range is required to satisfy reservable-container typename C::iterator begin() { return c.begin(); } typename C::iterator end() { return c.end(); } 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; AppendKind kind{}; bool used_reserve = false; }; void test_2_1_4() { // (2.1.4) constructible_from and // container-insertable> using Alloc = __gnu_test::uneq_allocator; using Alloc2 = __gnu_test::uneq_allocator; using V = std::vector; using List = std::list; std::list l{1u, 2u, 3u}; std::list l2{4l, 5l, 6l}; // use vector::emplace_back and vector::reserve auto c = std::ranges::to>(l); static_assert(std::is_same_v>); VERIFY( c.c == V(l.begin(), l.end()) ); VERIFY( c.kind == EmplaceBack ); VERIFY( c.used_reserve ); // use vector::emplace_back and vector::reserve auto c2 = std::ranges::to>(l2, Alloc(78)); static_assert(std::is_same_v>); VERIFY( c2.c == V(l2.begin(), l2.end()) ); VERIFY( c2.c.get_allocator() == Alloc(78) ); VERIFY( c2.kind == EmplaceBack ); VERIFY( c2.used_reserve ); // use list::emplace_back auto c3 = std::ranges::to>(c.c, Alloc2(99)); static_assert(std::is_same_v>); VERIFY( c3.c == List(l.begin(), l.end()) ); VERIFY( c3.c.get_allocator() == Alloc(99) ); VERIFY( c3.kind == EmplaceBack ); VERIFY( ! c3.used_reserve ); // use vector::push_back and vector::reserve auto c4 = std::ranges::to>(l); static_assert(std::is_same_v>); VERIFY( c4.c == V(l.begin(), l.end()) ); VERIFY( c4.kind == PushBack ); VERIFY( c4.used_reserve ); // use vector::push_back and vector::reserve auto c5 = std::ranges::to>(l2, Alloc(78)); static_assert(std::is_same_v>); VERIFY( c5.c == V(l2.begin(), l2.end()) ); VERIFY( c5.c.get_allocator() == Alloc(78) ); VERIFY( c5.kind == PushBack ); VERIFY( c5.used_reserve ); // use list::push_back auto c6 = std::ranges::to>(c.c, Alloc2(99)); static_assert(std::is_same_v>); VERIFY( c6.c == List(l.begin(), l.end()) ); VERIFY( c6.c.get_allocator() == Alloc(99) ); VERIFY( c6.kind == PushBack ); VERIFY( ! c6.used_reserve ); // use vector::emplace and vector::reserve auto c7 = std::ranges::to>(l); static_assert(std::is_same_v>); VERIFY( c7.c == V(l.begin(), l.end()) ); VERIFY( c7.kind == Emplace ); VERIFY( c7.used_reserve ); // use vector::emplace and vector::reserve auto c8 = std::ranges::to>(l2, Alloc(78)); static_assert(std::is_same_v>); VERIFY( c8.c == V(l2.begin(), l2.end()) ); VERIFY( c8.c.get_allocator() == Alloc(78) ); VERIFY( c8.kind == Emplace ); VERIFY( c8.used_reserve ); // use list::emplace auto c9 = std::ranges::to>(c.c, Alloc2(99)); static_assert(std::is_same_v>); VERIFY( c9.c == List(l.begin(), l.end()) ); VERIFY( c9.c.get_allocator() == Alloc(99) ); VERIFY( c9.kind == Emplace ); VERIFY( ! c9.used_reserve ); // use vector::insert and vector::reserve auto c10 = std::ranges::to>(l); static_assert(std::is_same_v>); VERIFY( c10.c == V(l.begin(), l.end()) ); VERIFY( c10.kind == Insert ); VERIFY( c10.used_reserve ); // use vector::insert and vector::reserve auto c11 = std::ranges::to>(l2, Alloc(78)); static_assert(std::is_same_v>); VERIFY( c11.c == V(l2.begin(), l2.end()) ); VERIFY( c11.c.get_allocator() == Alloc(78) ); VERIFY( c11.kind == Insert ); VERIFY( c11.used_reserve ); // use list::insert auto c12 = std::ranges::to>(c.c, Alloc2(99)); static_assert(std::is_same_v>); VERIFY( c12.c == List(l.begin(), l.end()) ); VERIFY( c12.c.get_allocator() == Alloc(99) ); VERIFY( c12.kind == Insert ); VERIFY( ! c12.used_reserve ); struct NoCopyPls { NoCopyPls(int) { } NoCopyPls(const NoCopyPls&) { throw; } }; // Uses list::emplace_back(const int&) not list::push_back(NoCopyPls&&). (void) std::ranges::to>(l); } void test_2_2() { // (2.2) input_range> 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; R input_ranges[]{R(s1), R(s2), R(s3)}; __gnu_test::test_input_range rr(input_ranges); namespace pmr = std::pmr; __gnu_test::memory_resource res; #if _GLIBCXX_USE_CXX11_ABI auto vvs = std::ranges::to>>(rr, &res); auto str_alloc = pmr::polymorphic_allocator(&res); #else auto vvs = std::ranges::to>>(rr, &res); auto str_alloc = std::allocator(); #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> v; auto r = std::views::all(std::move(v)); auto l = std::ranges::to>>(r); VERIFY(l.empty()); } void test_nodiscard() { std::vector v; std::ranges::to>(v); // { dg-warning "ignoring return" } std::ranges::to(v); // { dg-warning "ignoring return" } std::ranges::to>(); // { dg-warning "ignoring return" } std::ranges::to(); // { dg-warning "ignoring return" } } void test_constexpr() { constexpr int x = [](int i) { auto c1 = std::views::iota(1, i) | std::ranges::to>(); auto c2 = std::views::iota(i, 10) | std::ranges::to(); return c1[0] + c2[0]; }(5); static_assert(x == 6); } void test_sfinae() { // PR libstdc++/112802 [](auto x) { static_assert(!requires { std::ranges::to>()(x); }); static_assert(!requires { std::ranges::to()(x); }); }(0); } void test_composition() { // PR libstdc++/113068 auto adaptor = std::ranges::to() | std::ranges::to(); auto str = adaptor(" "); } 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(); test_constexpr(); test_sfinae(); test_composition(); }