On Mon, Oct 20, 2025 at 05:54:53PM +0200, Tomasz Kaminski wrote: > On Mon, Oct 20, 2025 at 5:17 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 > > > We usually put the PR reference before the changelog element, and also > reference > it in the commit title as [PR119721]. > > > * include/std/tuple: Add tuple<> comparison operators for > > empty tuple-like types. > > > We usually refer to functions modified in file here, and if you are adding > new > ones a simple "Defined" is sufficient. For example here it would be: > include/std/tuple (tuple<>::operator==, tuple<>::operator<=>): Define > See: https://gcc.gnu.org/wiki/ChangeLog > > > * testsuite/23_containers/tuple/comparison_operators/119721.cc: > > New test. > > > We want to keep a hard column limit here, below 80 lines. > > > > > Signed-off-by: Osama Abdelkader <[email protected]> > > --- > > > I missed some changes in the test. I have already picked the commit locally, > so I will post an updated patch. > > > v5: > > - made the operators hidden friends > > 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..b287694cc 100644 > > --- a/libstdc++-v3/include/std/tuple > > +++ b/libstdc++-v3/include/std/tuple > > @@ -1999,6 +1999,25 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > > 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 > > + // Note: These operators allow comparison with any empty tuple-like > > type, > > + // including array<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]] > > + 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) > > + friend constexpr strong_ordering > > + operator<=>(const tuple&, const _UTuple&) noexcept > > + { return strong_ordering::equal; } > > +#endif // C++23 > > }; > > > > #if !(__cpp_concepts && __cpp_consteval && __cpp_conditional_explicit) // > > !C++20 > > 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> > > > We use our internal VERIFY macro for test, defined in: > #include <testsuite_hooks.h> > > > > + > > +void test01() > > +{ > > + std::tuple<> t; > > + std::array<int, 0> a; > > + > > + // Basic comparison should work > > + assert(t == a); > > > These become VERIFY macros. > > > + 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 > > > >
Many thanks Tomasz for doing that yourself in v6 :) Thank you. Best regards, Osama
