// Verify P2321R2 "zip" enhancements to std::tuple. // { dg-do run { target c++23 } } // FIXME [!HOSTED]: avoidable std::allocator usage // { dg-require-effective-target hosted } // { dg-add-options no_pch } #include #if __cpp_lib_ranges_zip != 202110L # error "Feature-test macro __cpp_lib_ranges_zip has wrong value in " #endif #include #include using std::tuple; using std::pair; using std::allocator; using std::allocator_arg_t; using std::allocator_arg; namespace alloc { struct B01; struct B02; struct B03; struct B04; } template<> struct std::uses_allocator> : std::true_type { }; template<> struct std::uses_allocator> : std::true_type { }; template<> struct std::uses_allocator> : std::true_type { }; template<> struct std::uses_allocator> : std::true_type { }; struct A { }; constexpr bool test01() { struct B { bool v; constexpr B(A&) : v(true) { } }; // template // constexpr explicit(false) tuple(tuple&); tuple t1a; tuple t1b = t1a; VERIFY( std::get<0>(t1b).v ); tuple t2a0; tuple t2b0 = t2a0; VERIFY( std::get<0>(t2b0).v ); tuple t2a1; tuple t2b1 = t2a1; VERIFY( std::get<1>(t2b1).v ); tuple t3a0; tuple t3b0 = t3a0; VERIFY( std::get<0>(t3b0).v ); tuple t3a1; tuple t3b1 = t3a1; VERIFY( std::get<1>(t3b1).v ); tuple t3a2; tuple t3b2 = t3a2; VERIFY( std::get<2>(t3b2).v ); // template // constexpr explicit(false) tuple(pair&); pair p2a0; tuple p2b0 = p2a0; VERIFY( std::get<0>(p2b0).v ); pair p2a1; tuple p2b1 = p2a1; VERIFY( std::get<1>(p2b1).v ); return true; } namespace alloc { struct B01 { bool v; B01(A&); constexpr B01(allocator_arg_t, allocator, A&) : v(true) { } }; constexpr bool test01() { using B = B01; // template // constexpr explicit(false) // tuple(allocator_arg_t, const Alloc& a, tuple&); tuple t1a; tuple t1b = {allocator_arg, allocator{}, t1a}; VERIFY( std::get<0>(t1b).v ); tuple t2a0; tuple t2b0 = {allocator_arg, allocator{}, t2a0}; VERIFY( std::get<0>(t2b0).v ); tuple t2a1; tuple t2b1 = {allocator_arg, allocator{}, t2a1}; VERIFY( std::get<1>(t2b1).v ); tuple t3a0; tuple t3b0 = {allocator_arg, allocator{}, t3a0}; VERIFY( std::get<0>(t3b0).v ); tuple t3a1; tuple t3b1 = {allocator_arg, allocator{}, t3a1}; VERIFY( std::get<1>(t3b1).v ); tuple t3a2; tuple t3b2 = {allocator_arg, allocator{}, t3a2}; VERIFY( std::get<2>(t3b2).v ); // template // constexpr explicit(false) // tuple(allocator_arg_t, const Alloc& a, pair&); pair p2a0; tuple p2b0 = {allocator_arg, allocator{}, p2a0}; VERIFY( std::get<0>(p2b0).v ); pair p2a1; tuple p2b1 = {allocator_arg, allocator{}, p2a1}; VERIFY( std::get<1>(p2b1).v ); return true; } } constexpr bool test02() { struct B { bool v; explicit constexpr B(A&) : v(true) { } }; // template // constexpr explicit(true) tuple(tuple&); static_assert(!std::is_convertible_v&, tuple>); tuple t1a; tuple t1b(t1a); VERIFY( std::get<0>(t1b).v ); static_assert(!std::is_convertible_v&, tuple>); static_assert(!std::is_convertible_v&, tuple>); tuple t2a0; tuple t2b0(t2a0); VERIFY( std::get<0>(t2b0).v ); tuple t2a1; tuple t2b1(t2a1); VERIFY( std::get<1>(t2b1).v ); static_assert(!std::is_convertible_v&, tuple>); static_assert(!std::is_convertible_v&, tuple>); static_assert(!std::is_convertible_v&, tuple>); tuple t3a0; tuple t3b0(t3a0); VERIFY( std::get<0>(t3b0).v ); tuple t3a1; tuple t3b1(t3a1); VERIFY( std::get<1>(t3b1).v ); tuple t3a2; tuple t3b2(t3a2); VERIFY( std::get<2>(t3b2).v ); // template // constexpr explicit(true) tuple(pair&); static_assert(!std::is_convertible_v&, tuple>); static_assert(!std::is_convertible_v&, tuple>); pair p2a0; tuple p2b0(p2a0); VERIFY( std::get<0>(p2b0).v ); pair p2a1; tuple p2b1(p2a1); VERIFY( std::get<1>(p2b1).v ); return true; } namespace alloc { struct B02 { bool v; explicit B02(A&); explicit constexpr B02(allocator_arg_t, allocator, A&) : v(true) { } }; constexpr bool test02() { using B = B02; // template // constexpr explicit(true) // tuple(allocator_arg_t, const Alloc& a, tuple&); tuple t1a; tuple t1b(allocator_arg, allocator{}, t1a); VERIFY( std::get<0>(t1b).v ); tuple t2a0; tuple t2b0(allocator_arg, allocator{}, t2a0); VERIFY( std::get<0>(t2b0).v ); tuple t2a1; tuple t2b1(allocator_arg, allocator{}, t2a1); VERIFY( std::get<1>(t2b1).v ); tuple t3a0; tuple t3b0(allocator_arg, allocator{}, t3a0); VERIFY( std::get<0>(t3b0).v ); tuple t3a1; tuple t3b1(allocator_arg, allocator{}, t3a1); VERIFY( std::get<1>(t3b1).v ); tuple t3a2; tuple t3b2(allocator_arg, allocator{}, t3a2); VERIFY( std::get<2>(t3b2).v ); // template // constexpr explicit(true) // tuple(allocator_arg_t, const Alloc& a, pair&); pair p2a0; tuple p2b0(allocator_arg, allocator{}, p2a0); VERIFY( std::get<0>(p2b0).v ); pair p2a1; tuple p2b1(allocator_arg, allocator{}, p2a1); VERIFY( std::get<1>(p2b1).v ); return true; } } // namespace alloc constexpr bool test03() { struct B { bool v; constexpr B(const A&&) : v(true) { } }; // template // constexpr explicit(false) tuple(const tuple&&); const tuple t1a; tuple t1b = std::move(t1a); VERIFY( std::get<0>(t1b).v ); const tuple t2a0; tuple t2b0 = std::move(t2a0); VERIFY( std::get<0>(t2b0).v ); const tuple t2a1; tuple t2b1 = std::move(t2a1); VERIFY( std::get<1>(t2b1).v ); const tuple t3a0; tuple t3b0 = std::move(t3a0); VERIFY( std::get<0>(t3b0).v ); const tuple t3a1; tuple t3b1 = std::move(t3a1); VERIFY( std::get<1>(t3b1).v ); const tuple t3a2; tuple t3b2 = std::move(t3a2); VERIFY( std::get<2>(t3b2).v ); // template // constexpr explicit(false) tuple(const pair&&); const pair p2a0; tuple p2b0 = std::move(p2a0); VERIFY( std::get<0>(p2b0).v ); const pair p2a1; tuple p2b1 = std::move(p2a1); VERIFY( std::get<1>(p2b1).v ); return true; } namespace alloc { struct B03 { bool v; B03(const A&&); constexpr B03(allocator_arg_t, allocator, const A&&) : v(true) { } }; constexpr bool test03() { using B = B03; // template // constexpr explicit(false) // tuple(allocator_arg_t, const Alloc& a, const tuple&&); const tuple t1a; tuple t1b = {allocator_arg, allocator{}, std::move(t1a)}; VERIFY( std::get<0>(t1b).v ); const tuple t2a0; tuple t2b0 = {allocator_arg, allocator{}, std::move(t2a0)}; VERIFY( std::get<0>(t2b0).v ); const tuple t2a1; tuple t2b1 = {allocator_arg, allocator{}, std::move(t2a1)}; VERIFY( std::get<1>(t2b1).v ); const tuple t3a0; tuple t3b0 = {allocator_arg, allocator{}, std::move(t3a0)}; VERIFY( std::get<0>(t3b0).v ); const tuple t3a1; tuple t3b1 = {allocator_arg, allocator{}, std::move(t3a1)}; VERIFY( std::get<1>(t3b1).v ); const tuple t3a2; tuple t3b2 = {allocator_arg, allocator{}, std::move(t3a2)}; VERIFY( std::get<2>(t3b2).v ); // template // constexpr explicit(false) // tuple(allocator_arg_t, const Alloc& a, const pair&&); const pair p2a0; tuple p2b0 = {allocator_arg, allocator{}, std::move(p2a0)}; VERIFY( std::get<0>(p2b0).v ); const pair p2a1; tuple p2b1 = {allocator_arg, allocator{}, std::move(p2a1)}; VERIFY( std::get<1>(p2b1).v ); return true; } }; constexpr bool test04() { struct B { bool v; explicit constexpr B(const A&&) : v(true) { } }; // template // constexpr explicit(true) tuple(const tuple&&); static_assert(!std::is_convertible_v&, tuple>); const tuple t1a; tuple t1b(std::move(t1a)); VERIFY( std::get<0>(t1b).v ); static_assert(!std::is_convertible_v&, tuple>); static_assert(!std::is_convertible_v&, tuple>); const tuple t2a0; tuple t2b0(std::move(t2a0)); VERIFY( std::get<0>(t2b0).v ); const tuple t2a1; tuple t2b1(std::move(t2a1)); VERIFY( std::get<1>(t2b1).v ); static_assert(!std::is_convertible_v&, tuple>); static_assert(!std::is_convertible_v&, tuple>); static_assert(!std::is_convertible_v&, tuple>); const tuple t3a0; tuple t3b0(std::move(t3a0)); VERIFY( std::get<0>(t3b0).v ); const tuple t3a1; tuple t3b1(std::move(t3a1)); VERIFY( std::get<1>(t3b1).v ); const tuple t3a2; tuple t3b2(std::move(t3a2)); VERIFY( std::get<2>(t3b2).v ); // template // constexpr explicit(true) tuple(const pair&&); static_assert(!std::is_convertible_v&, tuple>); static_assert(!std::is_convertible_v&, tuple>); const pair p2a0; tuple p2b0(std::move(p2a0)); VERIFY( std::get<0>(p2b0).v ); const pair p2a1; tuple p2b1(std::move(p2a1)); VERIFY( std::get<1>(p2b1).v ); return true; } namespace alloc { struct B04 { bool v; explicit B04(const A&&); explicit constexpr B04(allocator_arg_t, allocator, const A&&) : v(true) { } }; constexpr bool test04() { using B = B04; // template // constexpr explicit(true) // tuple(allocator_arg_t, const Alloc& a, const tuple&&); const tuple t1a; tuple t1b(allocator_arg, allocator{}, std::move(t1a)); VERIFY( std::get<0>(t1b).v ); const tuple t2a0; tuple t2b0(allocator_arg, allocator{}, std::move(t2a0)); VERIFY( std::get<0>(t2b0).v ); const tuple t2a1; tuple t2b1(allocator_arg, allocator{}, std::move(t2a1)); VERIFY( std::get<1>(t2b1).v ); const tuple t3a0; tuple t3b0(allocator_arg, allocator{}, std::move(t3a0)); VERIFY( std::get<0>(t3b0).v ); const tuple t3a1; tuple t3b1(allocator_arg, allocator{}, std::move(t3a1)); VERIFY( std::get<1>(t3b1).v ); const tuple t3a2; tuple t3b2(allocator_arg, allocator{}, std::move(t3a2)); VERIFY( std::get<2>(t3b2).v ); // template // constexpr explicit(true) // tuple(allocator_arg_t, const Alloc& a, const pair&&); tuple p2b0(allocator_arg, allocator{}, std::move(t2a0)); VERIFY( std::get<0>(p2b0).v ); tuple p2b1(allocator_arg, allocator{}, std::move(t2a1)); VERIFY( std::get<1>(p2b1).v ); return true; } }; constexpr bool test05() { struct B { mutable bool v; constexpr const B& operator=(const A&) const { v = true; return *this; } }; // template // constexpr const tuple& operator=(const tuple&) const; const tuple t1a; const tuple t1b; t1b = t1a; VERIFY( std::get<0>(t1b).v ); const tuple t2a; const tuple t2b; t2b = t2a; VERIFY( std::get<0>(t2b).v ); VERIFY( std::get<1>(t2b).v ); const tuple t3a; const tuple t3b; t3b = t3a; VERIFY( std::get<0>(t3b).v ); VERIFY( std::get<1>(t3b).v ); VERIFY( std::get<2>(t3b).v ); // template // constexpr const tuple& operator=(const pair&) const; const pair p2a; const tuple p2b; p2b = p2a; return true; } constexpr bool test06() { struct B { mutable bool v; constexpr const B& operator=(A&&) const { v = true; return *this; } }; // template // constexpr const tuple& operator=(tuple&&) const; tuple t1a; const tuple t1b; t1b = std::move(t1a); VERIFY( std::get<0>(t1b).v ); tuple t2a; const tuple t2b; t2b = std::move(t2a); VERIFY( std::get<0>(t2b).v ); VERIFY( std::get<1>(t2b).v ); tuple t3a; const tuple t3b; t3b = std::move(t3a); VERIFY( std::get<0>(t3b).v ); VERIFY( std::get<1>(t3b).v ); VERIFY( std::get<2>(t3b).v ); // template // constexpr const tuple& operator=(pair&&) const; pair p2a; const tuple p2b; p2b = std::move(p2a); return true; } constexpr bool test07() { struct B { mutable bool v; constexpr const B& operator=(const B&) const { v = true; return *this; } }; // constexpr const tuple& operator=(const tuple&) const; const tuple t1a; const tuple t1b; t1b = t1a; VERIFY( std::get<0>(t1b).v ); const tuple t2a; const tuple t2b; t2b = t2a; VERIFY( std::get<0>(t2b).v ); VERIFY( std::get<1>(t2b).v ); const tuple t3a; const tuple t3b; t3b = t3a; VERIFY( std::get<0>(t3b).v ); VERIFY( std::get<1>(t3b).v ); VERIFY( std::get<2>(t3b).v ); return true; } constexpr bool test08() { struct B { mutable bool v; constexpr const B& operator=(B&&) const { v = true; return *this; } }; // constexpr const tuple& operator=(tuple&&) const; tuple t1a; const tuple t1b; t1b = std::move(t1a); VERIFY( std::get<0>(t1b).v ); tuple t2a; const tuple t2b; t2b = std::move(t2a); VERIFY( std::get<0>(t2b).v ); VERIFY( std::get<1>(t2b).v ); tuple t3a; const tuple t3b; t3b = std::move(t3a); VERIFY( std::get<0>(t3b).v ); VERIFY( std::get<1>(t3b).v ); VERIFY( std::get<2>(t3b).v ); return true; } struct S { mutable int v = 0; friend constexpr void swap(S&& x, S&& y) = delete; friend constexpr void swap(const S& x, const S& y) { ++x.v; ++y.v; } }; constexpr bool test09() { const tuple t1, u1; std::swap(t1, u1); VERIFY( std::get<0>(t1).v == 1 ); VERIFY( std::get<0>(u1).v == 1 ); const tuple t2, u2; std::swap(t2, u2); VERIFY( std::get<0>(t2).v == 1 ); VERIFY( std::get<0>(u2).v == 1 ); VERIFY( std::get<1>(t2).v == 1 ); VERIFY( std::get<1>(u2).v == 1 ); const tuple t3, u3; std::swap(t3, u3); VERIFY( std::get<0>(t3).v == 1 ); VERIFY( std::get<0>(u3).v == 1 ); VERIFY( std::get<1>(t3).v == 1 ); VERIFY( std::get<1>(u3).v == 1 ); VERIFY( std::get<2>(t3).v == 1 ); VERIFY( std::get<2>(u3).v == 1 ); static_assert(!std::is_swappable_v&>); return true; } int main() { static_assert(test01()); static_assert(alloc::test01()); static_assert(test02()); static_assert(alloc::test02()); static_assert(test03()); static_assert(alloc::test03()); static_assert(test04()); static_assert(alloc::test04()); // FIXME: G++ doesn't support reading mutable members during constexpr (PR c++/92505). test05(); test06(); test07(); test08(); test09(); }