From: Osama Abdelkader Date: Sat, 25 Oct 2025 17:25:42 +0000 (+0300) Subject: libstdc++: Add constructors and assignments for tuple<> with tuple-like types [PR119721] X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=52e300124d2eaad0f19ccc0a49f95fd605a1de69;p=thirdparty%2Fgcc.git libstdc++: Add constructors and assignments for tuple<> with tuple-like types [PR119721] This patch adds support for constructing and assigning tuple<> from other empty tuple-like types (e.g., array), completing the C++23 tuple-like interface for the zero-element tuple specialization. The implementation includes: - Constructor from forwarding reference to tuple-like types - Allocator-aware constructor from tuple-like types - Assignment operator from tuple-like types - Const assignment operator from tuple-like types PR libstdc++/119721 libstdc++-v3/ChangeLog: * include/std/tuple (tuple<>::tuple(const tuple&)) (tuple<>::operator=(const tuple&)): Define as defaulted. (tuple<>::swap): Moved the defintion after assignments. (tuple<>::tuple(_UTuple&&)) (tuple<>::tuple(allocator_arg_t, const _Alloc&, _UTuple&&)) (tuple<>::operator=(_UTuple&&)) [__cpp_lib_tuple_like]: Define. (tuple<>::operator==, tuple<>::opeator<=>): Parenthesize constrains individually. * testsuite/23_containers/tuple/cons/119721.cc: New test for constructors and assignments with empty tuple-like types. * testsuite/20_util/tuple/requirements/empty_trivial.cc: New test verifying tuple<> remains trivially copyable. Reviewed-by: Jonathan Wakely Co-authored-by: Tomasz Kamiński Signed-off-by: Osama Abdelkader Signed-off-by: Tomasz Kamiński --- diff --git a/libstdc++-v3/include/std/tuple b/libstdc++-v3/include/std/tuple index c064a92df4c1..6edcf5e55536 100644 --- a/libstdc++-v3/include/std/tuple +++ b/libstdc++-v3/include/std/tuple @@ -1984,14 +1984,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION 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 _GLIBCXX20_CONSTEXPR @@ -2001,16 +2009,42 @@ _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_tuple_v<_UTuple> && tuple_size_v<_UTuple> == 0) + requires (!is_same_v, tuple>) + && (!is_same_v, allocator_arg_t>) + && (tuple_size_v> == 0) + constexpr + tuple(_UTuple&&) noexcept { } + + template + requires (!is_same_v, tuple>) + && (tuple_size_v> == 0) + constexpr + tuple(allocator_arg_t, const _Alloc&, _UTuple&&) noexcept { } + + template<__tuple_like _UTuple> + requires (!is_same_v, tuple>) + && (tuple_size_v> == 0) + constexpr tuple& + operator=(_UTuple&&) noexcept + { return *this; } + + template<__tuple_like _UTuple> + requires (!is_same_v, tuple>) + && (tuple_size_v> == 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; } diff --git a/libstdc++-v3/testsuite/20_util/tuple/requirements/empty_trivial.cc b/libstdc++-v3/testsuite/20_util/tuple/requirements/empty_trivial.cc new file mode 100644 index 000000000000..ee18bb3145ea --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/tuple/requirements/empty_trivial.cc @@ -0,0 +1,17 @@ +// { dg-do compile { target c++11 } } + +#include +#include + +// Check that tuple<> has the expected trivial properties +static_assert(std::is_trivially_copyable>::value, + "tuple<> should be trivially copyable"); +static_assert(std::is_trivially_copy_constructible>::value, + "tuple<> should be trivially copy constructible"); +static_assert(std::is_trivially_move_constructible>::value, + "tuple<> should be trivially move constructible"); +static_assert(std::is_trivially_copy_assignable>::value, + "tuple<> should be trivially copy assignable"); +static_assert(std::is_trivially_move_assignable>::value, + "tuple<> should be trivially move assignable"); + 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 000000000000..240f1a5e8d78 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/tuple/cons/119721.cc @@ -0,0 +1,111 @@ +// { dg-do run { target c++23 } } + +// Test for PR libstdc++/119721: tuple<> construction/assignment with array + +#include +#include +#include +#include + +constexpr void +test01() +{ + std::array a{}; + + // Constructor from array + std::tuple<> t1(a); + std::tuple<> t2(std::move(a)); + + // Assignment from array + 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 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 a{}; + std::tuple<>& r3 = (t = a); + VERIFY( &r3 == &t ); +} + +constexpr void +test04() +{ + std::array a{}; + const std::tuple<> t1; + + // Const assignment from array + t1 = a; + t1 = std::move(a); + + VERIFY( t1 == a ); +} + +void +test05() +{ + std::array a{}; + std::allocator 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; +} +