class tuple<>
{
public:
+ // We need the default since we're going to define no-op
+ // allocator constructors.
+ tuple() = default;
+ // Defaulted copy operations to maintain trivial copyability.
+ // and support non-const assignment expressions.
+ tuple(const tuple&) = default;
+ tuple& operator=(const tuple&) = default;
+
_GLIBCXX20_CONSTEXPR
void swap(tuple&) noexcept { /* no-op */ }
+
#if __cpp_lib_ranges_zip // >= C++23
- constexpr void swap(const tuple&) const noexcept { /* no-op */ }
+ constexpr void swap(const tuple&) const noexcept
+ { /* no-op */ }
#endif
- // We need the default since we're going to define no-op
- // allocator constructors.
- tuple() = default;
+
// No-op allocator constructors.
template<typename _Alloc>
_GLIBCXX20_CONSTEXPR
tuple(allocator_arg_t, const _Alloc&, const tuple&) noexcept { }
#if __cpp_lib_tuple_like // >= C++23
- // Comparison operators for tuple<> with other empty tuple-like types
template<__tuple_like _UTuple>
- requires (!__is_tuple_v<_UTuple> && tuple_size_v<_UTuple> == 0)
+ requires (!is_same_v<remove_cvref_t<_UTuple>, tuple>)
+ && (!is_same_v<remove_cvref_t<_UTuple>, allocator_arg_t>)
+ && (tuple_size_v<remove_cvref_t<_UTuple>> == 0)
+ constexpr
+ tuple(_UTuple&&) noexcept { }
+
+ template<typename _Alloc, __tuple_like _UTuple>
+ requires (!is_same_v<remove_cvref_t<_UTuple>, tuple>)
+ && (tuple_size_v<remove_cvref_t<_UTuple>> == 0)
+ constexpr
+ tuple(allocator_arg_t, const _Alloc&, _UTuple&&) noexcept { }
+
+ template<__tuple_like _UTuple>
+ requires (!is_same_v<remove_cvref_t<_UTuple>, tuple>)
+ && (tuple_size_v<remove_cvref_t<_UTuple>> == 0)
+ constexpr tuple&
+ operator=(_UTuple&&) noexcept
+ { return *this; }
+
+ template<__tuple_like _UTuple>
+ requires (!is_same_v<remove_cvref_t<_UTuple>, tuple>)
+ && (tuple_size_v<remove_cvref_t<_UTuple>> == 0)
+ constexpr const tuple&
+ operator=(_UTuple&&) const noexcept
+ { return *this; }
+
+ template<__tuple_like _UTuple>
+ requires (!__is_tuple_v<_UTuple>) && (tuple_size_v<_UTuple> == 0)
[[nodiscard]]
friend constexpr bool
operator==(const tuple&, const _UTuple&) noexcept
{ return true; }
template<__tuple_like _UTuple>
- requires (!__is_tuple_v<_UTuple> && tuple_size_v<_UTuple> == 0)
+ requires (!__is_tuple_v<_UTuple>) && (tuple_size_v<_UTuple> == 0)
friend constexpr strong_ordering
operator<=>(const tuple&, const _UTuple&) noexcept
{ return strong_ordering::equal; }
--- /dev/null
+// { dg-do compile { target c++11 } }
+
+#include <tuple>
+#include <type_traits>
+
+// Check that tuple<> has the expected trivial properties
+static_assert(std::is_trivially_copyable<std::tuple<>>::value,
+ "tuple<> should be trivially copyable");
+static_assert(std::is_trivially_copy_constructible<std::tuple<>>::value,
+ "tuple<> should be trivially copy constructible");
+static_assert(std::is_trivially_move_constructible<std::tuple<>>::value,
+ "tuple<> should be trivially move constructible");
+static_assert(std::is_trivially_copy_assignable<std::tuple<>>::value,
+ "tuple<> should be trivially copy assignable");
+static_assert(std::is_trivially_move_assignable<std::tuple<>>::value,
+ "tuple<> should be trivially move assignable");
+
--- /dev/null
+// { dg-do run { target c++23 } }
+
+// Test for PR libstdc++/119721: tuple<> construction/assignment with array<T, 0>
+
+#include <tuple>
+#include <array>
+#include <memory>
+#include <testsuite_hooks.h>
+
+constexpr void
+test01()
+{
+ std::array<int, 0> a{};
+
+ // Constructor from array<int, 0>
+ std::tuple<> t1(a);
+ std::tuple<> t2(std::move(a));
+
+ // Assignment from array<int, 0>
+ std::tuple<> t3;
+ t3 = a;
+ t3 = std::move(a);
+
+ VERIFY( t1 == a );
+ VERIFY( t2 == a );
+ VERIFY( t3 == a );
+}
+
+constexpr void
+test02()
+{
+ // Test with non-comparable element type
+ struct NonComparable
+ {
+ void operator==(const NonComparable&) const = delete;
+ void operator<=>(const NonComparable&) const = delete;
+ };
+
+ std::array<NonComparable, 0> a{};
+
+ std::tuple<> t1(a);
+ std::tuple<> t2(std::move(a));
+
+ std::tuple<> t3;
+ t3 = a;
+ t3 = std::move(a);
+
+ VERIFY( t1 == a );
+}
+
+constexpr void
+test03()
+{
+ // Test assignment return type (non-const assignment)
+ std::tuple<> t, u;
+ std::tuple<>& r1 = (t = u);
+ VERIFY( &r1 == &t );
+
+ std::tuple<>& r2 = (t = {});
+ VERIFY( &r2 == &t );
+
+ std::array<int, 0> a{};
+ std::tuple<>& r3 = (t = a);
+ VERIFY( &r3 == &t );
+}
+
+constexpr void
+test04()
+{
+ std::array<int, 0> a{};
+ const std::tuple<> t1;
+
+ // Const assignment from array
+ t1 = a;
+ t1 = std::move(a);
+
+ VERIFY( t1 == a );
+}
+
+void
+test05()
+{
+ std::array<int, 0> a{};
+ std::allocator<int> alloc;
+
+ // Allocator constructor from array
+ std::tuple<> t1(std::allocator_arg, alloc, a);
+ std::tuple<> t2(std::allocator_arg, alloc, std::move(a));
+
+ VERIFY( t1 == a );
+ VERIFY( t2 == a );
+}
+
+int main()
+{
+ auto test_all = [] {
+ test01();
+ test02();
+ test03();
+ test04();
+ return true;
+ };
+
+ test_all();
+ static_assert( test_all() );
+
+ // allocator test is not constexpr
+ test05();
+ return 0;
+}
+