On Mon, Oct 20, 2025 at 10:13 AM Tomasz Kaminski <[email protected]> wrote:
> > > On Sat, Oct 18, 2025 at 9:51 PM Osama Abdelkader < > [email protected]> wrote: > >> This fixes the C++23 compliance issue where std::tuple<> cannot be >> compared >> with other empty tuple-like types such as std::array<T, 0>. >> >> The operators correctly allow comparison with array<T, 0> even when T is >> not >> comparable, because empty tuple-like types don't compare element values. >> >> libstdc++-v3/ChangeLog: >> >> PR libstdc++/119721 >> * include/std/tuple: Add tuple<> comparison operators for >> empty tuple-like types. >> * testsuite/23_containers/tuple/comparison_operators/119721.cc: >> New test. >> >> Signed-off-by: Osama Abdelkader <[email protected]> >> --- >> v4: >> - Added testsuite test >> v3: >> - Added noexcept specifiers to the operators >> v2: >> - Replaced explicit array<T, 0> operators with generic tuple-like >> operators >> - Only operator== and operator<=> are provided >> - Operators work with any empty tuple-like type >> - No need for reversed argument order >> --- >> libstdc++-v3/include/std/tuple | 19 +++++ >> .../tuple/comparison_operators/119721.cc | 71 +++++++++++++++++++ >> 2 files changed, 90 insertions(+) >> create mode 100644 >> libstdc++-v3/testsuite/23_containers/tuple/comparison_operators/119721.cc >> >> diff --git a/libstdc++-v3/include/std/tuple >> b/libstdc++-v3/include/std/tuple >> index 0ca616f1b..0709cf7b3 100644 >> --- a/libstdc++-v3/include/std/tuple >> +++ b/libstdc++-v3/include/std/tuple >> @@ -2001,6 +2001,25 @@ _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 >> + // Note: These operators allow comparison with any empty tuple-like >> type, >> + // including array<T, 0> and span<T, 0>, where T may not be comparable. >> + // This is correct because empty tuple-like types don't compare >> elements. >> + template<__tuple_like _UTuple> >> + requires (!__is_tuple_v<_UTuple> && tuple_size_v<_UTuple> == 0) >> + [[nodiscard]] >> + constexpr bool >> + operator==(const tuple<>&, const _UTuple&) noexcept >> + { return true; } >> + >> + template<__tuple_like _UTuple> >> + requires (!__is_tuple_v<_UTuple> && tuple_size_v<_UTuple> == 0) >> + constexpr strong_ordering >> + operator<=>(const tuple<>&, const _UTuple&) noexcept >> + { return strong_ordering::equal; } >> +#endif // C++23 > > Thanks for the update, this looks good to me, only one minor suggestion. > For consistency with normal tuple specialization, I would declare them as > hidden friends. > This mean putting them inside tuple<> specialization and declaring as:: > + 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; } > // We do not need tuple<>, as we can use injected class names. > > I have realized that we are also missing the _UTuple&& constructors and > assignment, > if this is something you are interested in adding, please feel free to > submit a separate patch. > What we need are following, with additional requirements on being > tuple_like and size 0 > tuple(_UTuple&&) -> !is_same_v<remove_cvref_t<_UTuple, tuple>> && > !is_same_v<remove_cvref_t<_UTuple>, allocator_arg_t> > tuple(allocator_arg_t, _UTuple&&) -> !is_same_v<remove_cvref_t<_UTuple, > tuple>> > > tuple& operator=(_UTuple&&); -> !is_same_v<remove_cvref_t<_UTuple, tuple>> > tuple const& operator=(_UTuple&&) const; > -> !is_same_v<remove_cvref_t<_UTuple, tuple>> > > And also const copy-assigment matching swap: > tuple const& operator=(tuple const&) const; > And if we add above we need to default usual copy assignment: > tuple& operator=(tuple const&) = default; > Otherwise is_trivially_copy/move_assignagble will become false. > > + >> #if !(__cpp_concepts && __cpp_consteval && __cpp_conditional_explicit) >> // !C++20 >> /// Partial specialization, 2-element tuple. >> /// Includes construction and assignment from a pair. >> diff --git >> a/libstdc++-v3/testsuite/23_containers/tuple/comparison_operators/119721.cc >> b/libstdc++-v3/testsuite/23_containers/tuple/comparison_operators/119721.cc >> new file mode 100644 >> index 000000000..711874acf >> --- /dev/null >> +++ >> b/libstdc++-v3/testsuite/23_containers/tuple/comparison_operators/119721.cc >> @@ -0,0 +1,71 @@ >> +// { dg-do compile { target c++23 } } >> +// { dg-options "-std=c++23" } >> + >> +// Test for PR libstdc++/119721: tuple<> comparison with array<T, 0> >> + >> +#include <tuple> >> +#include <array> >> +#include <cassert> >> + >> +void test01() >> +{ >> + std::tuple<> t; >> + std::array<int, 0> a; >> + >> + // Basic comparison should work >> + assert(t == a); >> + assert(a == t); >> + assert(!(t != a)); >> + assert(!(a != t)); >> + >> + // Ordering comparisons should be equal >> + assert(!(t < a)); >> + assert(!(t > a)); >> + assert(t <= a); >> + assert(t >= a); >> + assert(!(a < t)); >> + assert(!(a > t)); >> + assert(a <= t); >> + assert(a >= t); >> + >> + // Three-way comparison should return equal >> + assert((t <=> a) == std::strong_ordering::equal); >> + assert((a <=> t) == std::strong_ordering::equal); >> +} >> + >> +void test02() >> +{ >> + // Test with non-comparable element type >> + struct NonComparable { >> + void operator==(const NonComparable&) const = delete; >> + void operator<=>(const NonComparable&) const = delete; >> + }; >> + >> + std::tuple<> t; >> + std::array<NonComparable, 0> a; >> + >> + // Should still work because empty containers don't compare elements >> + assert(t == a); >> + assert((t <=> a) == std::strong_ordering::equal); >> +} >> + >> +void test03() >> +{ >> + // Test constexpr evaluation >> + constexpr std::tuple<> t; >> + constexpr std::array<int, 0> a; >> + >> + constexpr bool eq = t == a; >> + constexpr auto cmp = t <=> a; >> + >> + static_assert(eq == true); >> + static_assert(cmp == std::strong_ordering::equal); >> +} >> + >> +int main() >> +{ >> + test01(); >> + test02(); >> + test03(); >> + return 0; >> +} >> -- >> 2.43.0 >> >>
