This patch adds missing constructors and assignment operators for tuple<>
to support construction and assignment from other empty tuple-like types
such as array<T, 0>, completing the C++23 tuple-like support.

The following members are added:
- tuple(_UTuple&&): Constructor from empty tuple-like types
- tuple(allocator_arg_t, const _Alloc&, _UTuple&&): Allocator constructor
- operator=(_UTuple&&): Assignment from empty tuple-like types
- operator=(const tuple&) const: Const copy assignment (matching swap)
- operator=(_UTuple&&) const: Const assignment from tuple-like types

All operators are constexpr and noexcept, with appropriate constraints to
avoid ambiguity with existing constructors/assignments.

Note: The addition of const assignment operators means tuple<> is no longer
trivially copyable in C++23, which is consistent with the general tuple
template that also has const assignment operators for ranges/zip support.

        PR libstdc++/119721

libstdc++-v3/ChangeLog:

        * include/std/tuple (tuple<>::tuple(_UTuple&&)): Define.
        (tuple<>::tuple(allocator_arg_t, const _Alloc&, _UTuple&&)): Define.
        (tuple<>::operator=(_UTuple&&)): Define.
        (tuple<>::operator=(const tuple&) const): Define.
        (tuple<>::operator=(_UTuple&&) const): Define.
        * testsuite/23_containers/tuple/cons/119721.cc: New test.

Suggested-by: Tomasz Kami??ski <[email protected]>
Signed-off-by: Osama Abdelkader <[email protected]>
---
 libstdc++-v3/include/std/tuple                |  32 +++++-
 .../23_containers/tuple/cons/119721.cc        | 102 ++++++++++++++++++
 2 files changed, 133 insertions(+), 1 deletion(-)
 create mode 100644 libstdc++-v3/testsuite/23_containers/tuple/cons/119721.cc

diff --git a/libstdc++-v3/include/std/tuple b/libstdc++-v3/include/std/tuple
index c064a92df..a78455d62 100644
--- a/libstdc++-v3/include/std/tuple
+++ b/libstdc++-v3/include/std/tuple
@@ -2001,7 +2001,37 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        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_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; }
+
+      constexpr const tuple&
+      operator=(const tuple&) const 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]]
diff --git a/libstdc++-v3/testsuite/23_containers/tuple/cons/119721.cc 
b/libstdc++-v3/testsuite/23_containers/tuple/cons/119721.cc
new file mode 100644
index 000000000..69f6d4d43
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/tuple/cons/119721.cc
@@ -0,0 +1,102 @@
+// { dg-do compile { target c++23 } }
+// { dg-options "-std=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()
+{
+  std::array<int, 0> a{};
+  const std::tuple<> t1;
+  
+  // Const copy assignment from tuple
+  std::tuple<> t2;
+  t1 = t2;
+  
+  // Const assignment from array
+  t1 = a;
+  t1 = std::move(a);
+  
+  VERIFY( t1 == t2 );
+  VERIFY( t1 == a );
+}
+
+void
+test04()
+{
+  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 );
+}
+
+// Note: In C++23, tuple<> is no longer trivially copyable due to
+// the const copy assignment operator added to match swap().
+
+int main()
+{
+  auto test_all = [] {
+    test01();
+    test02();
+    test03();
+  };
+
+  test_all();
+  static_VERIFY( test_all() );
+  
+  // allocator test is not constexpr
+  test04(); 
+  return 0;
+}
+
-- 
2.43.0

Reply via email to